Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Nim
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
Python
Racket
Ruby
Rust
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Zig
Javascript
GIMPLE
Ygen
c++ source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502-c++ 11.1.0
ARM GCC 10.2.0
ARM GCC 10.3.0
ARM GCC 10.4.0
ARM GCC 10.5.0
ARM GCC 11.1.0
ARM GCC 11.2.0
ARM GCC 11.3.0
ARM GCC 11.4.0
ARM GCC 12.1.0
ARM GCC 12.2.0
ARM GCC 12.3.0
ARM GCC 12.4.0
ARM GCC 13.1.0
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 13.3.0
ARM GCC 13.3.0 (unknown-eabi)
ARM GCC 14.1.0
ARM GCC 14.1.0 (unknown-eabi)
ARM GCC 14.2.0
ARM GCC 14.2.0 (unknown-eabi)
ARM GCC 4.5.4
ARM GCC 4.6.4
ARM GCC 5.4
ARM GCC 6.3.0
ARM GCC 6.4.0
ARM GCC 7.3.0
ARM GCC 7.5.0
ARM GCC 8.2.0
ARM GCC 8.5.0
ARM GCC 9.3.0
ARM GCC 9.4.0
ARM GCC 9.5.0
ARM GCC trunk
ARM gcc 10.2.1 (none)
ARM gcc 10.3.1 (2021.07 none)
ARM gcc 10.3.1 (2021.10 none)
ARM gcc 11.2.1 (none)
ARM gcc 5.4.1 (none)
ARM gcc 7.2.1 (none)
ARM gcc 8.2 (WinCE)
ARM gcc 8.3.1 (none)
ARM gcc 9.2.1 (none)
ARM msvc v19.0 (WINE)
ARM msvc v19.10 (WINE)
ARM msvc v19.14 (WINE)
ARM64 Morello gcc 10.1 Alpha 2
ARM64 gcc 10.2
ARM64 gcc 10.3
ARM64 gcc 10.4
ARM64 gcc 10.5.0
ARM64 gcc 11.1
ARM64 gcc 11.2
ARM64 gcc 11.3
ARM64 gcc 11.4.0
ARM64 gcc 12.1
ARM64 gcc 12.2.0
ARM64 gcc 12.3.0
ARM64 gcc 12.4.0
ARM64 gcc 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 13.3.0
ARM64 gcc 14.1.0
ARM64 gcc 14.2.0
ARM64 gcc 4.9.4
ARM64 gcc 5.4
ARM64 gcc 5.5.0
ARM64 gcc 6.3
ARM64 gcc 6.4
ARM64 gcc 7.3
ARM64 gcc 7.5
ARM64 gcc 8.2
ARM64 gcc 8.5
ARM64 gcc 9.3
ARM64 gcc 9.4
ARM64 gcc 9.5
ARM64 gcc trunk
ARM64 msvc v19.14 (WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 12.4.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF clang 19.1.0
EDG (experimental reflection)
EDG 6.5
EDG 6.5 (GNU mode gcc 13)
EDG 6.6
EDG 6.6 (GNU mode gcc 13)
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
KVX ACB 5.0.0 (GCC 12.2.1)
KVX ACB 5.2.0 (GCC 13.2.1)
LoongArch64 clang (trunk)
LoongArch64 clang 17.0.1
LoongArch64 clang 18.1.0
LoongArch64 clang 19.1.0
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68k clang (trunk)
MRISC32 gcc (trunk)
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 12.4.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 13.3.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 12.4.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 13.3.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 19.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 19.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 12.4.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI CL430 21.6.1
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.20 VS16.0
arm64 msvc v19.21 VS16.1
arm64 msvc v19.22 VS16.2
arm64 msvc v19.23 VS16.3
arm64 msvc v19.24 VS16.4
arm64 msvc v19.25 VS16.5
arm64 msvc v19.27 VS16.7
arm64 msvc v19.28 VS16.8
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30 VS17.0
arm64 msvc v19.31 VS17.1
arm64 msvc v19.32 VS17.2
arm64 msvc v19.33 VS17.3
arm64 msvc v19.34 VS17.4
arm64 msvc v19.35 VS17.5
arm64 msvc v19.36 VS17.6
arm64 msvc v19.37 VS17.7
arm64 msvc v19.38 VS17.8
arm64 msvc v19.39 VS17.9
arm64 msvc v19.40 VS17.10
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 12.0.0
armv7-a clang 12.0.1
armv7-a clang 13.0.0
armv7-a clang 13.0.1
armv7-a clang 14.0.0
armv7-a clang 15.0.0
armv7-a clang 16.0.0
armv7-a clang 17.0.1
armv7-a clang 18.1.0
armv7-a clang 19.1.0
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 13.0.0
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 19.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clang-cl 18.1.0
ellcc 0.1.33
ellcc 0.1.34
ellcc 2017-07-16
hexagon-clang 16.0.5
llvm-mos atari2600-3e
llvm-mos atari2600-4k
llvm-mos atari2600-common
llvm-mos atari5200-supercart
llvm-mos atari8-cart-megacart
llvm-mos atari8-cart-std
llvm-mos atari8-cart-xegs
llvm-mos atari8-common
llvm-mos atari8-dos
llvm-mos c128
llvm-mos c64
llvm-mos commodore
llvm-mos cpm65
llvm-mos cx16
llvm-mos dodo
llvm-mos eater
llvm-mos mega65
llvm-mos nes
llvm-mos nes-action53
llvm-mos nes-cnrom
llvm-mos nes-gtrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos nes-unrom
llvm-mos nes-unrom-512
llvm-mos osi-c1p
llvm-mos pce
llvm-mos pce-cd
llvm-mos pce-common
llvm-mos pet
llvm-mos rp6502
llvm-mos rpc8e
llvm-mos supervision
llvm-mos vic20
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 12.4.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips clang 19.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 12.4.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 12.4.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 clang 19.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 12.4.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4.0
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mips64el clang 19.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel clang 19.1.0
mipsel gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 12.4.0
mipsel gcc 13.1.0
mipsel gcc 13.2.0
mipsel gcc 13.3.0
mipsel gcc 14.1.0
mipsel gcc 14.2.0
mipsel gcc 4.9.4
mipsel gcc 5.4.0
mipsel gcc 5.5.0
mipsel gcc 9.5.0
nanoMIPS gcc 6.3.0 (mtk)
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 12.4.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64 gcc 11.2.0
power64 gcc 12.1.0
power64 gcc 12.2.0
power64 gcc 12.3.0
power64 gcc 12.4.0
power64 gcc 13.1.0
power64 gcc 13.2.0
power64 gcc 13.3.0
power64 gcc 14.1.0
power64 gcc 14.2.0
power64 gcc trunk
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 12.4.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
qnx 8.0.0
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 12.4.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 14.1.0
s390x gcc 14.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (WINE)
x64 msvc v19.10 (WINE)
x64 msvc v19.14 (WINE)
x64 msvc v19.20 VS16.0
x64 msvc v19.21 VS16.1
x64 msvc v19.22 VS16.2
x64 msvc v19.23 VS16.3
x64 msvc v19.24 VS16.4
x64 msvc v19.25 VS16.5
x64 msvc v19.27 VS16.7
x64 msvc v19.28 VS16.8
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30 VS17.0
x64 msvc v19.31 VS17.1
x64 msvc v19.32 VS17.2
x64 msvc v19.33 VS17.3
x64 msvc v19.34 VS17.4
x64 msvc v19.35 VS17.5
x64 msvc v19.36 VS17.6
x64 msvc v19.37 VS17.7
x64 msvc v19.38 VS17.8
x64 msvc v19.39 VS17.9
x64 msvc v19.40 VS17.10
x64 msvc v19.latest
x86 djgpp 4.9.4
x86 djgpp 5.5.0
x86 djgpp 6.4.0
x86 djgpp 7.2.0
x86 msvc v19.0 (WINE)
x86 msvc v19.10 (WINE)
x86 msvc v19.14 (WINE)
x86 msvc v19.20 VS16.0
x86 msvc v19.21 VS16.1
x86 msvc v19.22 VS16.2
x86 msvc v19.23 VS16.3
x86 msvc v19.24 VS16.4
x86 msvc v19.25 VS16.5
x86 msvc v19.27 VS16.7
x86 msvc v19.28 VS16.8
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30 VS17.0
x86 msvc v19.31 VS17.1
x86 msvc v19.32 VS17.2
x86 msvc v19.33 VS17.3
x86 msvc v19.34 VS17.4
x86 msvc v19.35 VS17.5
x86 msvc v19.36 VS17.6
x86 msvc v19.37 VS17.7
x86 msvc v19.38 VS17.8
x86 msvc v19.39 VS17.9
x86 msvc v19.40 VS17.10
x86 msvc v19.latest
x86 nvc++ 22.11
x86 nvc++ 22.7
x86 nvc++ 22.9
x86 nvc++ 23.1
x86 nvc++ 23.11
x86 nvc++ 23.3
x86 nvc++ 23.5
x86 nvc++ 23.7
x86 nvc++ 23.9
x86 nvc++ 24.1
x86 nvc++ 24.11
x86 nvc++ 24.3
x86 nvc++ 24.5
x86 nvc++ 24.7
x86 nvc++ 24.9
x86-64 Zapcc 190308
x86-64 clang (Chris Bazley N3089)
x86-64 clang (EricWF contracts)
x86-64 clang (amd-staging)
x86-64 clang (assertions trunk)
x86-64 clang (clangir)
x86-64 clang (dascandy contracts)
x86-64 clang (experimental -Wlifetime)
x86-64 clang (experimental P1061)
x86-64 clang (experimental P1144)
x86-64 clang (experimental P1221)
x86-64 clang (experimental P2996)
x86-64 clang (experimental P2998)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3367)
x86-64 clang (experimental P3372)
x86-64 clang (experimental metaprogramming - P2632)
x86-64 clang (old concepts branch)
x86-64 clang (p1974)
x86-64 clang (pattern matching - P2688)
x86-64 clang (reflection)
x86-64 clang (resugar)
x86-64 clang (string interpolation - P3412)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (variadic friends - P2893)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.0 (assertions)
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.0 (assertions)
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.0 (assertions)
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.0 (assertions)
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 14.0.0 (assertions)
x86-64 clang 15.0.0
x86-64 clang 15.0.0 (assertions)
x86-64 clang 16.0.0
x86-64 clang 16.0.0 (assertions)
x86-64 clang 17.0.1
x86-64 clang 17.0.1 (assertions)
x86-64 clang 18.1.0
x86-64 clang 18.1.0 (assertions)
x86-64 clang 18.1.0 (clad 1.8)
x86-64 clang 19.1.0
x86-64 clang 19.1.0 (assertions)
x86-64 clang 2.6.0 (assertions)
x86-64 clang 2.7.0 (assertions)
x86-64 clang 2.8.0 (assertions)
x86-64 clang 2.9.0 (assertions)
x86-64 clang 3.0.0
x86-64 clang 3.0.0 (assertions)
x86-64 clang 3.1
x86-64 clang 3.1 (assertions)
x86-64 clang 3.2
x86-64 clang 3.2 (assertions)
x86-64 clang 3.3
x86-64 clang 3.3 (assertions)
x86-64 clang 3.4 (assertions)
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5 (assertions)
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.6 (assertions)
x86-64 clang 3.7
x86-64 clang 3.7 (assertions)
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8 (assertions)
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.0 (assertions)
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.0 (assertions)
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.0 (assertions)
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.0 (assertions)
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.0 (assertions)
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.0 (assertions)
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.0 (assertions)
x86-64 clang 9.0.1
x86-64 clang rocm-4.5.2
x86-64 clang rocm-5.0.2
x86-64 clang rocm-5.1.3
x86-64 clang rocm-5.2.3
x86-64 clang rocm-5.3.3
x86-64 clang rocm-5.7.0
x86-64 clang rocm-6.0.2
x86-64 clang rocm-6.1.2
x86-64 gcc (contract labels)
x86-64 gcc (contracts natural syntax)
x86-64 gcc (contracts)
x86-64 gcc (coroutines)
x86-64 gcc (modules)
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.3 (assertions)
x86-64 gcc 10.4
x86-64 gcc 10.4 (assertions)
x86-64 gcc 10.5
x86-64 gcc 10.5 (assertions)
x86-64 gcc 11.1
x86-64 gcc 11.1 (assertions)
x86-64 gcc 11.2
x86-64 gcc 11.2 (assertions)
x86-64 gcc 11.3
x86-64 gcc 11.3 (assertions)
x86-64 gcc 11.4
x86-64 gcc 11.4 (assertions)
x86-64 gcc 12.1
x86-64 gcc 12.1 (assertions)
x86-64 gcc 12.2
x86-64 gcc 12.2 (assertions)
x86-64 gcc 12.3
x86-64 gcc 12.3 (assertions)
x86-64 gcc 12.4
x86-64 gcc 12.4 (assertions)
x86-64 gcc 13.1
x86-64 gcc 13.1 (assertions)
x86-64 gcc 13.2
x86-64 gcc 13.2 (assertions)
x86-64 gcc 13.3
x86-64 gcc 13.3 (assertions)
x86-64 gcc 14.1
x86-64 gcc 14.1 (assertions)
x86-64 gcc 14.2
x86-64 gcc 14.2 (assertions)
x86-64 gcc 3.4.6
x86-64 gcc 4.0.4
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 5.5
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.4
x86-64 gcc 6.5
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2023.2.1
x86-64 icx 2024.0.0
x86-64 icx 2024.1.0
x86-64 icx 2024.2.0
x86-64 icx 2025.0.0
x86-64 icx 2025.0.0
zig c++ 0.10.0
zig c++ 0.11.0
zig c++ 0.12.0
zig c++ 0.12.1
zig c++ 0.13.0
zig c++ 0.6.0
zig c++ 0.7.0
zig c++ 0.7.1
zig c++ 0.8.0
zig c++ 0.9.0
zig c++ trunk
Options
Source code
/* test.h Copyright 2018 Mathias Westerdahl * * https://github.com/JCash/jctest * https://jcash.github.io/jctest * * BRIEF: * * A single header only C/C++ test framework in <1kloc * Made sure to compile with highest warning/error levels possible * * HISTORY: * * 0.3 2019-04-25 Ansi colors for Win32 * Msys2 + Cygwin support * setjmp fix for Emscripten * Removed limit on number of tests * 0.2 2019-04-14 Fixed ASSERT_EQ for single precision floats * 0.1 2019-01-19 Added GTEST-like C++ interface * * LICENSE: * * The MIT License (MIT) * * Copyright (c) 2018-2019 Mathias Westerdahl * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * DISCLAIMER: * * This software is supplied "AS IS" without any warranties and support * * This software was designed to be a (non complete) replacement for GTEST, * with the intent to replace the library in an existing code base. * Although the supported features were implemented in the same spirit as the GTEST * fixtures/functions, there will be discprepancies. However, those differences have * been chosen from a pragmatic standpoint, in favor of making porting of the existing * tests feasible with minimal changes, as well as keeping this library * as light weight as possible. * * * USAGE: * For more use cases, see end of document // Smallest test case #define JC_TEST_IMPLEMENTATION #include <jc_test.h> TEST(FixtureName, TestName) { ASSERT_EQ(4, 2*2); } int main(int argc, char** argv) { jc_test_init(&argc, argv); return jc_test_run_all(); } */ #ifndef JC_TEST_H #define JC_TEST_H // *************************************************************************************** // PUBLIC API // May modify the argument list, to remove the test specific arguments extern void jc_test_init(int* argc, char** argv); // Runs all registered tests extern int jc_test_run_all(); // The standard test class struct jc_test_base_class { virtual ~jc_test_base_class(); static void SetUpTestCase() {} // The UserClass::SetUpTestCase is called before each test case runs static void TearDownTestCase() {} // The UserClass::TearDownTestCase is called after all tests have run virtual void SetUp(); // Called before each test virtual void TearDown(); // Called after each test virtual void TestBody() = 0; // Implemented by TEST_F and TEST_P private: struct Setup_should_be_spelled_SetUp {}; virtual Setup_should_be_spelled_SetUp* Setup() { return 0; } // Trick from GTEST to make sure users don't accidentally misspell the function }; // A parameterized test class, to use with TEST_P and INSTANTIATE_TEST_CASE_P template<typename ParamType> struct jc_test_params_class : public jc_test_base_class { typedef ParamType param_t; jc_test_params_class() {} static const ParamType& GetParam() { return *param; } static void SetParam(const ParamType* _param) { param = _param; } static const ParamType* param; }; // *************************************************************************************** // TEST API // Basic test // #define TEST(testfixture,testfn) // // Basic tests using a user defined fixture class (jc_test_base_class) // #define TEST_F(testfixture,testfn) // // Parameterized tests using a user defined fixture class (jc_test_params_class<T>) // #define TEST_P(testfixture,testfn) // // Instantiation of parameterisaed test // #define INSTANTIATE_TEST_CASE_P(prefix,testfixture,testvalues) // *************************************************************************************** // ASSERTION API // #define SKIP() Skips the test // Fatal failures // #define ASSERT_TRUE( VALUE ) value // #define ASSERT_FALSE( VALUE ) !value // #define ASSERT_EQ( A, B ) A == B // #define ASSERT_NE( A, B ) A != B // #define ASSERT_LT( A, B ) A < B // #define ASSERT_GT( A, B ) A > B // #define ASSERT_LE( A, B ) A <= B // #define ASSERT_GE( A, B ) A >= B // #define ASSERT_STREQ( A, B ) strcmp(A,B) == 0 // #define ASSERT_STRNE( A, B ) strcmp(A,B) != 0 // #define ASSERT_NEAR( A, B, EPSILON ) abs(a - b) < epsilon // #define ASSERT_DEATH(S, RE) // #define EXPECT_TRUE( VALUE ) value // #define EXPECT_FALSE( VALUE ) !value // #define EXPECT_EQ( A, B ) A == B // #define EXPECT_NE( A, B ) A != B // #define EXPECT_LT( A, B ) A < B // #define EXPECT_GT( A, B ) A > B // #define EXPECT_LE( A, B ) A <= B // #define EXPECT_GE( A, B ) A >= B // #define EXPECT_STREQ( A, B ) strcmp(A,B) == 0 // #define EXPECT_STRNE( A, B ) strcmp(A,B) != 0 // #define EXPECT_NEAR( A, B, EPS ) abs(a - b) < epsilon // #define EXPECT_DEATH(S, RE) // #define SCOPED_TRACE(_MSG) // nop // *************************************************************************************** // Possible modifications for the included files // Can be used to override the logging entirely (e.g. for writing html output) #ifndef JC_TEST_LOGF #include <stdarg.h> //va_list // Can be overridden to log in a different way #define JC_TEST_LOGF jc_test_logf #endif #ifndef JC_TEST_SNPRINTF #include <stdio.h> //snprintf #if defined(_MSC_VER) #define JC_TEST_SNPRINTF _snprintf #else #define JC_TEST_SNPRINTF snprintf #endif #endif #ifndef JC_TEST_ASSERT_FN #include <assert.h> #define JC_TEST_ASSERT_FN assert #endif #ifndef JC_TEST_EXIT #include <stdlib.h> #define JC_TEST_EXIT exit #endif #ifndef JC_TEST_NO_DEATH_TEST #include <signal.h> #include <setjmp.h> // setjmp+longjmp #if defined(__EMSCRIPTEN__) || defined(__MINGW32__) #define JC_TEST_SETJMP setjmp #else #define JC_TEST_SETJMP _setjmp #endif #endif // C++0x and above #if !defined(_MSC_VER) #pragma GCC diagnostic push #if !defined(__GNUC__) #if __cplusplus >= 199711L // Silencing them made the code unreadable, so I opted to disable them instead #pragma GCC diagnostic ignored "-Wc++98-compat" #endif #endif #if __cplusplus >= 201103L #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif #endif #if defined(__GNUC__) || defined(__clang__) #define JC_TEST_UNUSED __attribute__ ((unused)) #else #define JC_TEST_UNUSED #endif #if !defined(JC_TEST_UINT64) #include <stdint.h> #define JC_TEST_UINT64 uint64_t #define JC_TEST_UINT32 uint32_t #endif #define JC_TEST_EVENT_FIXTURE_SETUP 0 #define JC_TEST_EVENT_FIXTURE_TEARDOWN 1 #define JC_TEST_EVENT_TEST_SETUP 2 #define JC_TEST_EVENT_TEST_TEARDOWN 3 #define JC_TEST_EVENT_ASSERT_FAILED 4 #define JC_TEST_EVENT_SUMMARY 5 #define JC_TEST_EVENT_GENERIC 6 #ifndef JC_TEST_TIMING_FUNC #define JC_TEST_TIMING_FUNC jc_test_get_time // returns micro seconds typedef unsigned long jc_test_time_t; extern jc_test_time_t jc_test_get_time(void); #endif // Returns the user defined context for the fixture typedef void* (*jc_test_fixture_setup_func)(); struct jc_test_factory_base_interface; typedef void (*jc_test_void_staticfunc)(); typedef void (jc_test_base_class::*jc_test_void_memberfunc)(); typedef struct jc_test_entry { jc_test_entry* next; // linked list const char* name; jc_test_base_class* instance; jc_test_factory_base_interface* factory; // Factory for parameterized tests JC_TEST_UINT32 fail:1; JC_TEST_UINT32 skipped:1; JC_TEST_UINT32 :30; #if defined(__x86_64__) || defined(__ppc64__) || defined(_WIN64) JC_TEST_UINT32 :32; #endif } jc_test_entry; typedef struct jc_test_stats { int num_pass; int num_fail:16; int num_skipped:16; int num_assertions; int num_tests; jc_test_time_t totaltime; } jc_test_stats; typedef struct jc_test_fixture { virtual ~jc_test_fixture(); virtual void SetParam(); jc_test_fixture* next; // linked list jc_test_entry* tests; // linked list const char* name; // The name of the fixture const char* filename; // The filename of the current ASSERT/EXPECT struct jc_test_fixture* parent; // In case of parameterized tests, this points to the first test jc_test_void_staticfunc fixture_setup; jc_test_void_staticfunc fixture_teardown; jc_test_stats stats; unsigned int fail:27; unsigned int type:2; // 0: function, 1: class, 2: params class, 3: params instance unsigned int first:1; // If it's the first in a range of fixtures unsigned int last:1; // If it's the last in a range of fixtures unsigned int fatal:1; // If set, it aborts the test unsigned int index; // the index of the param in the original params array int signum:8; // If we're checking for a signal int line:16; // The line of the current ASSERT/EXPECT int _pad:8; int num_tests; } jc_test_fixture; typedef struct jc_test_state { #if !defined(JC_TEST_NO_DEATH_TEST) jmp_buf jumpenv; // Set before trying to catch exceptions // remove padding warning due to jmp_buf struct alignment unsigned char _pad[sizeof(jmp_buf)+sizeof(void*) - (sizeof(jmp_buf)/sizeof(void*))*sizeof(void*)]; #endif jc_test_entry* current_test; jc_test_fixture* current_fixture; jc_test_fixture* fixtures; jc_test_stats stats; int num_fixtures:31; unsigned int is_a_tty:1; unsigned int :32; } jc_test_state; // *************************************************************************************** // Private functions extern jc_test_state* jc_test_get_state(); extern void jc_test_exit(); // called by jc_test_run_all extern void jc_test_set_test_fail(int fatal); extern void jc_test_set_test_skipped(); extern void jc_test_increment_assertions(); extern void jc_test_set_signal_handler(); extern void jc_test_unset_signal_handler(); extern int jc_test_streq(const char* a, const char* b); extern void jc_test_logf(const jc_test_fixture* fixture, const jc_test_entry* test, const jc_test_stats* stats, int event, const char* format, ...); extern int jc_test_cmp_double_eq(double, double); extern int jc_test_cmp_float_eq(float, float); extern int jc_test_cmp_STREQ(const char* a, const char* b, const char* exprA, const char* exprB); extern int jc_test_cmp_STRNE(const char* a, const char* b, const char* exprA, const char* exprB); extern int jc_test_cmp_NEAR(double a, double b, double epsilon, const char* exprA, const char* exprB, const char* exprC); #define JC_TEST_CAST(_TYPE_, _EXPR_) reinterpret_cast< _TYPE_ >( _EXPR_ ) #define JC_TEST_STATIC_CAST(_TYPE_, _EXPR_) static_cast< _TYPE_ >( _EXPR_ ) static inline jc_test_fixture* jc_test_get_fixture() { return jc_test_get_state()->current_fixture; } static inline jc_test_entry* jc_test_get_test() { return jc_test_get_state()->current_test; } template <typename T> char* jc_test_print_value(char* buffer, size_t, const T) { buffer[0] = '?'; buffer[1] = 0; return buffer+2; } template <> char* jc_test_print_value(char* buffer, size_t buffer_len, const double value); template <> char* jc_test_print_value(char* buffer, size_t buffer_len, const float value); template <> char* jc_test_print_value(char* buffer, size_t buffer_len, const int value); template <> char* jc_test_print_value(char* buffer, size_t buffer_len, const unsigned int value); template <> char* jc_test_print_value(char* buffer, size_t buffer_len, const char* value); template <typename T1, typename T2> static inline void jc_test_log_failure(T1 a, T2 b, const char* exprA, const char* exprB, const char* op) { char bufferA[64]; jc_test_print_value(bufferA, sizeof(bufferA), a); char bufferB[64]; jc_test_print_value(bufferB, sizeof(bufferB), b); JC_TEST_LOGF(jc_test_get_fixture(), jc_test_get_test(), 0, JC_TEST_EVENT_ASSERT_FAILED, "\nExpected: (%s) %s (%s), actual: %s vs %s\n", exprA, op, exprB, bufferA, bufferB); } template <typename T> static inline void jc_test_log_failure_boolean(T v, const char* expr) { JC_TEST_LOGF(jc_test_get_fixture(), jc_test_get_test(), 0, JC_TEST_EVENT_ASSERT_FAILED, "\nValue of: %s\nExpected: %s\n Actual: %s\n", expr, (!v)?"true":"false", v?"true":"false"); } template <typename T> static inline void jc_test_log_failure_str(const T* a, const T* b, const char* exprA, const char* exprB, const char* op) { JC_TEST_LOGF(jc_test_get_fixture(), jc_test_get_test(), 0, JC_TEST_EVENT_ASSERT_FAILED, "\nValue of: %s %s %s\nExpected: %s\n Actual: %s\n", exprA, op, exprB, a, b); } template <typename T> int jc_test_cmp_TRUE(T v, const char* expr) { if (v) return 1; jc_test_log_failure_boolean(v, expr); return 0; } template <typename T> int jc_test_cmp_FALSE(T v, const char* expr) { if (!v) return 1; jc_test_log_failure_boolean(v, expr); return 0; } #define JC_TEST_COMPARE_FUNC(OP_NAME, OP) \ template <typename T1, typename T2> \ int jc_test_cmp_ ## OP_NAME(T1 a, T2 b, const char* exprA, const char* exprB) { \ if (a OP b) return 1; \ jc_test_log_failure(a, b, exprA, exprB, #OP); \ return 0; \ } JC_TEST_COMPARE_FUNC(EQ, ==) JC_TEST_COMPARE_FUNC(NE, !=) JC_TEST_COMPARE_FUNC(LE, <=) JC_TEST_COMPARE_FUNC(LT, <) JC_TEST_COMPARE_FUNC(GE, >=) JC_TEST_COMPARE_FUNC(GT, >) template <typename T> int jc_test_cmp_EQ(double a, T b, const char* exprA, const char* exprB) { if (jc_test_cmp_double_eq(a, JC_TEST_STATIC_CAST(double, b))) return 1; jc_test_log_failure(a, b, exprA, exprB, "=="); return 0; } template <typename T> int jc_test_cmp_EQ(float a, T b, const char* exprA, const char* exprB) { if (jc_test_cmp_float_eq(a, JC_TEST_STATIC_CAST(float, b))) return 1; jc_test_log_failure(a, b, exprA, exprB, "=="); return 0; } template <typename T> int jc_test_cmp_NE(double a, T b, const char* exprA, const char* exprB) { if (!jc_test_cmp_double_eq(a, JC_TEST_STATIC_CAST(double, b))) return 1; jc_test_log_failure(a, b, exprA, exprB, "!="); return 0; } template <typename T> int jc_test_cmp_NE(float a, T b, const char* exprA, const char* exprB) { if (!jc_test_cmp_float_eq(a, JC_TEST_STATIC_CAST(float, b))) return 1; jc_test_log_failure(a, b, exprA, exprB, "!="); return 0; } // The only way to match this function, is with a null literal (since the type is hidden) char _jc_test_is_null_literal(struct _jc_test_null_literal* p); char (&_jc_test_is_null_literal(...))[2]; #define JC_TEST_IS_NULL_LITERAL(VALUE) (sizeof(_jc_test_is_null_literal(VALUE)) == 1) template <typename T> struct jc_test_is_pointer { static const bool value = false; }; template <typename T> struct jc_test_is_pointer<T*> { static const bool value = true; }; template <bool> struct jc_test_enable_argument; template <> struct jc_test_enable_argument<true> { typedef void type; }; template<bool is_null_literal> struct jc_test_cmp_eq_helper { template<typename T1, typename T2> static int compare(const T1& a, const T2& b, const char* exprA, const char* exprB) { return jc_test_cmp_EQ(a, b, exprA, exprB); } }; template<> struct jc_test_cmp_eq_helper<true> { template<typename T1, typename T2> static int compare(const T1& a, const T2& b, const char* exprA, const char* exprB, typename jc_test_enable_argument<!jc_test_is_pointer<T2>::value>::type* = 0) { return jc_test_cmp_EQ(a, b, exprA, exprB); } template<typename T> static int compare(_jc_test_null_literal*, T* b, const char* exprA, const char* exprB) { return jc_test_cmp_EQ(JC_TEST_STATIC_CAST(T*, 0), b, exprA, exprB); } }; #define JC_TEST_ASSERT_SETUP \ jc_test_get_fixture()->line = __LINE__; \ jc_test_get_fixture()->filename = __FILE__; \ jc_test_increment_assertions() #define JC_TEST_FATAL_FAILURE jc_test_set_test_fail(1); return #define JC_TEST_NON_FATAL_FAILURE jc_test_set_test_fail(0); #define JC_ASSERT_TEST_BOOLEAN(OP, VALUE, FAIL_FUNC) \ do { \ JC_TEST_ASSERT_SETUP; \ if ( jc_test_cmp_##OP (VALUE, #VALUE) == 0 ) { \ FAIL_FUNC; \ } \ } while(0) #define JC_ASSERT_TEST_EQ(A, B, FAIL_FUNC) \ do { \ JC_TEST_ASSERT_SETUP; \ if ( jc_test_cmp_eq_helper<JC_TEST_IS_NULL_LITERAL(A)>::compare(A, B, #A, #B) == 0 ) { \ FAIL_FUNC; \ } \ } while(0) #define JC_ASSERT_TEST_OP(OP, A, B, FAIL_FUNC) \ do { \ JC_TEST_ASSERT_SETUP; \ if ( jc_test_cmp_##OP (A, B, #A, #B) == 0 ) { \ FAIL_FUNC; \ } \ } while(0) #define JC_ASSERT_TEST_3OP(OP, A, B, C, FAIL_FUNC) \ do { \ JC_TEST_ASSERT_SETUP; \ if ( jc_test_cmp_##OP (A, B, C, #A, #B, #C) == 0 ) { \ FAIL_FUNC; \ } \ } while(0) #if !defined(JC_TEST_NO_DEATH_TEST) #define JC_ASSERT_TEST_DEATH_OP(STATEMENT, RE, FAIL_FUNC) \ do { \ JC_TEST_ASSERT_SETUP; \ if (JC_TEST_SETJMP(jc_test_get_state()->jumpenv) == 0) { \ jc_test_set_signal_handler(); \ JC_TEST_LOGF(jc_test_get_fixture(), jc_test_get_test(), 0, JC_TEST_EVENT_GENERIC, "\njc_test: Death test begin ->\n"); \ STATEMENT; \ JC_TEST_LOGF(jc_test_get_fixture(), jc_test_get_test(), 0, JC_TEST_EVENT_ASSERT_FAILED, "\nExpected this to fail: %s", #STATEMENT ); \ jc_test_unset_signal_handler(); \ FAIL_FUNC; \ } \ jc_test_unset_signal_handler(); \ JC_TEST_LOGF(jc_test_get_fixture(), jc_test_get_test(), 0, JC_TEST_EVENT_GENERIC, "jc_test: <- Death test end\n"); \ } while(0) #else #define JC_ASSERT_TEST_DEATH_OP(STATEMENT, RE, FAIL_FUNC) #endif // TEST API Begin --> #define SKIP() { jc_test_set_test_skipped(); return; } #define ASSERT_TRUE( VALUE ) JC_ASSERT_TEST_BOOLEAN( TRUE, VALUE, JC_TEST_FATAL_FAILURE ) #define ASSERT_FALSE( VALUE ) JC_ASSERT_TEST_BOOLEAN( FALSE, VALUE, JC_TEST_FATAL_FAILURE ) #define ASSERT_EQ( A, B ) JC_ASSERT_TEST_EQ( A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_NE( A, B ) JC_ASSERT_TEST_OP( NE, A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_LT( A, B ) JC_ASSERT_TEST_OP( LT, A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_GT( A, B ) JC_ASSERT_TEST_OP( GT, A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_LE( A, B ) JC_ASSERT_TEST_OP( LE, A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_GE( A, B ) JC_ASSERT_TEST_OP( GE, A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_STREQ( A, B ) JC_ASSERT_TEST_OP( STREQ, A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_STRNE( A, B ) JC_ASSERT_TEST_OP( STRNE, A, B, JC_TEST_FATAL_FAILURE ) #define ASSERT_NEAR( A, B, EPS ) JC_ASSERT_TEST_3OP( NEAR, A, B, EPS, JC_TEST_FATAL_FAILURE ) #define ASSERT_DEATH(S, RE) JC_ASSERT_TEST_DEATH_OP( S, RE, JC_TEST_FATAL_FAILURE ) #define EXPECT_TRUE( VALUE ) JC_ASSERT_TEST_BOOLEAN( TRUE, VALUE, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_FALSE( VALUE ) JC_ASSERT_TEST_BOOLEAN( FALSE, VALUE, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_EQ( A, B ) JC_ASSERT_TEST_EQ( A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_NE( A, B ) JC_ASSERT_TEST_OP( NE, A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_LT( A, B ) JC_ASSERT_TEST_OP( LT, A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_GT( A, B ) JC_ASSERT_TEST_OP( GT, A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_LE( A, B ) JC_ASSERT_TEST_OP( LE, A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_GE( A, B ) JC_ASSERT_TEST_OP( GE, A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_STREQ( A, B ) JC_ASSERT_TEST_OP( STREQ, A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_STRNE( A, B ) JC_ASSERT_TEST_OP( STRNE, A, B, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_NEAR( A, B, EPS ) JC_ASSERT_TEST_3OP( NEAR, A, B, EPS, JC_TEST_NON_FATAL_FAILURE ) #define EXPECT_DEATH(S, RE) JC_ASSERT_TEST_DEATH_OP(S, RE, JC_TEST_NON_FATAL_FAILURE ) #define SCOPED_TRACE(_MSG) // nop template<typename T> struct jc_test_value_iterator { virtual ~jc_test_value_iterator(); virtual const T* Get() const = 0; virtual void Advance() = 0; virtual bool Empty() const = 0; // return false when out of values virtual void Rewind() = 0; }; template<typename T> jc_test_value_iterator<T>::~jc_test_value_iterator() {} // separate line to silence warning template<typename T> struct jc_test_array_iterator : public jc_test_value_iterator<T> { const T *begin, *cursor, *end; jc_test_array_iterator(const T* _begin, const T* _end) : begin(_begin), cursor(_begin), end(_end) {} const T* Get() const{ return cursor; } void Advance() { ++cursor; } bool Empty() const { return cursor == end; } void Rewind() { cursor = begin; } }; template<typename T> jc_test_array_iterator<T>* jc_test_values_in(const T* begin, const T* end) { return new jc_test_array_iterator<T>(begin, end); } template<typename T, size_t N> jc_test_array_iterator<T>* jc_test_values_in(const T (&arr)[N] ) { return jc_test_values_in(arr, arr+N); } template<typename ParamType> const ParamType* jc_test_params_class<ParamType>::param = 0; template<typename ParamType> struct jc_test_fixture_with_param : public jc_test_fixture { void SetParam() { JC_TEST_CAST(jc_test_params_class<ParamType>*, jc_test_get_state()->current_test)->SetParam(param); } const ParamType* param; }; struct jc_test_factory_base_interface { virtual ~jc_test_factory_base_interface(); }; template<typename ParamType> struct jc_test_factory_interface : public jc_test_factory_base_interface { virtual jc_test_params_class<ParamType>* New() = 0; virtual void SetParam(const ParamType* param) = 0; }; template<typename T> struct jc_test_factory : public jc_test_factory_interface<typename T::param_t> { jc_test_params_class<typename T::param_t>* New() { return new T(); } void SetParam(const typename T::param_t* param) { T::SetParam(param); } }; #define JC_TEST_FIXTURE_TYPE_CLASS 0 #define JC_TEST_FIXTURE_TYPE_PARAMS_CLASS 1 #define JC_TEST_FIXTURE_TYPE_TYPED_CLASS 2 extern jc_test_fixture* jc_test_find_fixture(const char* name, unsigned int fixture_type); extern jc_test_fixture* jc_test_alloc_fixture(const char* name, unsigned int fixture_type); extern jc_test_fixture* jc_test_create_fixture(jc_test_fixture* fixture, const char* name, unsigned int fixture_type); extern jc_test_entry* jc_test_add_test_to_fixture(jc_test_fixture* fixture, const char* test_name, jc_test_base_class* instance, jc_test_factory_base_interface* factory); extern void jc_test_memcpy(void* dst, void* src, size_t size); extern int jc_test_register_class_test(const char* fixture_name, const char* test_name, jc_test_void_staticfunc class_setup, jc_test_void_staticfunc class_teardown, jc_test_base_class* instance, unsigned int fixture_type); template <typename ParamType> int jc_test_register_param_class_test(const char* fixture_name, const char* test_name, jc_test_void_staticfunc class_setup, jc_test_void_staticfunc class_teardown, jc_test_factory_interface<ParamType>* factory) { jc_test_fixture* fixture = jc_test_find_fixture(fixture_name, JC_TEST_FIXTURE_TYPE_PARAMS_CLASS); if (!fixture) { fixture = jc_test_alloc_fixture(fixture_name, JC_TEST_FIXTURE_TYPE_PARAMS_CLASS); fixture->fixture_setup = class_setup; fixture->fixture_teardown = class_teardown; } jc_test_add_test_to_fixture(fixture, test_name, 0, factory); return 0; } struct jc_test_type0 {}; template<typename T1> struct jc_test_type1 { typedef T1 head; typedef jc_test_type0 tail; }; template<typename T1, typename T2> struct jc_test_type2 { typedef T2 head; typedef jc_test_type1<T1> tail; }; template<typename T1, typename T2, typename T3> struct jc_test_type3 { typedef T3 head; typedef jc_test_type2<T1, T2> tail; }; template<typename T1, typename T2, typename T3, typename T4> struct jc_test_type4 { typedef T4 head; typedef jc_test_type3<T1, T2, T3> tail; }; template <typename BaseClassSelector, typename TypeList> struct jc_test_register_typed_class_test { static int register_test(const char* fixture_name, const char* test_name, unsigned int index) { typedef typename TypeList::head TypeParam; typedef typename BaseClassSelector::template bind<TypeParam>::type TestClass; jc_test_fixture* fixture = jc_test_find_fixture(fixture_name, JC_TEST_FIXTURE_TYPE_TYPED_CLASS); if (!fixture) { fixture = jc_test_alloc_fixture(fixture_name, JC_TEST_FIXTURE_TYPE_CLASS); fixture->fixture_setup = TestClass::SetUpTestCase; fixture->fixture_teardown = TestClass::TearDownTestCase; } fixture->index = index; jc_test_add_test_to_fixture(fixture, test_name, new TestClass, 0); return jc_test_register_typed_class_test<BaseClassSelector, typename TypeList::tail>:: register_test(fixture_name, test_name, index+1); } }; template <typename BaseClassSelector> struct jc_test_register_typed_class_test<BaseClassSelector,jc_test_type0> { static int register_test(const char*, const char*, unsigned int) { return 0; } }; template<typename ParamType> jc_test_fixture* jc_test_alloc_fixture_with_param(const char* name, unsigned int type) { return jc_test_create_fixture(new jc_test_fixture_with_param<ParamType>, name, type); } template<typename ParamType> int jc_test_register_param_tests(const char* prototype_fixture_name, const char* fixture_name, jc_test_value_iterator<ParamType>* values) { unsigned int index = 0; jc_test_fixture* first_fixture = 0; while (!values->Empty()) { jc_test_fixture* prototype_fixture = jc_test_find_fixture(prototype_fixture_name, JC_TEST_FIXTURE_TYPE_PARAMS_CLASS); if (!prototype_fixture) { JC_TEST_LOGF(0, 0, 0, JC_TEST_EVENT_GENERIC, "Couldn't find fixture of name %s\n", prototype_fixture_name); JC_TEST_ASSERT_FN(prototype_fixture != 0); return 0; } JC_TEST_ASSERT_FN(prototype_fixture->type == JC_TEST_FIXTURE_TYPE_PARAMS_CLASS); // Allocate a new fixture, and create the test class jc_test_fixture_with_param<ParamType>* fixture = JC_TEST_CAST(jc_test_fixture_with_param<ParamType>*, jc_test_alloc_fixture_with_param<ParamType>(fixture_name, JC_TEST_FIXTURE_TYPE_CLASS) ); fixture->first = first_fixture == 0 ? 1 : 0; if (!first_fixture) { first_fixture = fixture; // A silly trick to make the first fixture accumulate all the timings from this batch } fixture->parent = first_fixture; fixture->index = index++; fixture->param = values->Get(); fixture->fixture_setup = prototype_fixture->fixture_setup; fixture->fixture_teardown = prototype_fixture->fixture_teardown; fixture->num_tests = prototype_fixture->num_tests; jc_test_entry* prototype_test = prototype_fixture->tests; jc_test_entry* first = 0; jc_test_entry* prev = 0; while (prototype_test) { jc_test_entry* test = new jc_test_entry; test->next = 0; test->name = prototype_test->name; test->factory = 0; test->fail = 0; test->skipped = 0; jc_test_factory_interface<ParamType>* factory = JC_TEST_CAST(jc_test_factory_interface<ParamType>*, prototype_test->factory); factory->SetParam(fixture->param); test->instance = factory->New(); if (!first) { first = test; prev = test; } else { prev->next = test; prev = test; } prototype_test = prototype_test->next; } fixture->tests = first; values->Advance(); fixture->last = values->Empty() ? 1 : 0; } delete values; return 0; } #define JC_TEST_MAKE_NAME2(X,Y) X ## _ ## Y #define JC_TEST_MAKE_NAME3(X,Y,Z) X ## _ ## Y ## _ ## Z #define JC_TEST_MAKE_CLASS_NAME(X, Y) JC_TEST_MAKE_NAME3(X, Y, _TestCase) #define JC_TEST_MAKE_FUNCTION_NAME(X, Y) JC_TEST_MAKE_NAME2(X, Y) #define JC_TEST_MAKE_UNIQUE_NAME(X, Y, LINE) JC_TEST_MAKE_NAME3(X, Y, LINE) #define TEST(testfixture,testfn) \ class JC_TEST_MAKE_CLASS_NAME(testfixture,testfn) : public jc_test_base_class { \ virtual void TestBody(); \ }; \ static int JC_TEST_MAKE_UNIQUE_NAME(testfixture,testfn,__LINE__) JC_TEST_UNUSED = jc_test_register_class_test( \ #testfixture, #testfn, jc_test_base_class::SetUpTestCase, jc_test_base_class::TearDownTestCase, \ new JC_TEST_MAKE_CLASS_NAME(testfixture,testfn), JC_TEST_FIXTURE_TYPE_CLASS); \ void JC_TEST_MAKE_CLASS_NAME(testfixture,testfn)::TestBody() #define TEST_F(testfixture,testfn) \ class JC_TEST_MAKE_CLASS_NAME(testfixture,testfn) : public testfixture { \ virtual void TestBody(); \ }; \ static int JC_TEST_MAKE_UNIQUE_NAME(testfixture,testfn,__LINE__) JC_TEST_UNUSED = jc_test_register_class_test( \ #testfixture, #testfn, testfixture::SetUpTestCase, testfixture::TearDownTestCase, \ new JC_TEST_MAKE_CLASS_NAME(testfixture,testfn), JC_TEST_FIXTURE_TYPE_CLASS); \ void JC_TEST_MAKE_CLASS_NAME(testfixture,testfn)::TestBody() #define TEST_P(testfixture,testfn) \ class JC_TEST_MAKE_CLASS_NAME(testfixture,testfn) : public testfixture { \ virtual void TestBody(); \ }; \ static int JC_TEST_MAKE_UNIQUE_NAME(testfixture,testfn,__LINE__) JC_TEST_UNUSED = jc_test_register_param_class_test( \ #testfixture, #testfn, testfixture::SetUpTestCase, testfixture::TearDownTestCase, \ new jc_test_factory<JC_TEST_MAKE_CLASS_NAME(testfixture,testfn)>()); \ void JC_TEST_MAKE_CLASS_NAME(testfixture,testfn)::TestBody() #define INSTANTIATE_TEST_CASE_P(prefix,testfixture,testvalues) \ static int JC_TEST_MAKE_UNIQUE_NAME(prefix,testfixture,__LINE__) JC_TEST_UNUSED = \ jc_test_register_param_tests<testfixture::param_t>(#testfixture, #prefix "/" #testfixture, testvalues) template<typename T> struct jc_test_typed_list { typedef T type; }; template<template <typename T> class BaseClass> struct jc_test_template_sel { template <typename TypeParam> struct bind { typedef BaseClass<TypeParam> type; }; }; #define TYPED_TEST_CASE(testfixture,testtypes) \ typedef jc_test_typed_list<testtypes>::type JC_TEST_MAKE_NAME2(testfixture,Types) #define TYPED_TEST(testfixture,testfn) \ template<typename T> class JC_TEST_MAKE_CLASS_NAME(testfixture,testfn) : public testfixture<T> { \ virtual void TestBody(); \ typedef testfixture<T> TestFixture; \ typedef T TypeParam; \ }; \ static int JC_TEST_MAKE_UNIQUE_NAME(testfixture,testfn,__LINE__) JC_TEST_UNUSED = \ jc_test_register_typed_class_test< \ jc_test_template_sel<JC_TEST_MAKE_CLASS_NAME(testfixture,testfn)>, \ JC_TEST_MAKE_NAME2(testfixture,Types)>::register_test(#testfixture, #testfn, 0); \ template<typename T> void JC_TEST_MAKE_CLASS_NAME(testfixture,testfn)<T>::TestBody() #if !defined(_MSC_VER) #pragma GCC diagnostic pop #endif #endif // JC_TEST_H #ifdef JC_TEST_IMPLEMENTATION #undef JC_TEST_IMPLEMENTATION #if !defined(_MSC_VER) #pragma GCC diagnostic push #if !defined(__GNUC__) #endif #if __cplusplus >= 201103L #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif #endif #define JC_TEST_PRINT_TYPE_FN(TYPE, FORMAT) \ template <> char* jc_test_print_value(char* buffer, size_t buffer_len, const TYPE value) { \ return buffer + JC_TEST_SNPRINTF(buffer, buffer_len, FORMAT, value); \ } JC_TEST_PRINT_TYPE_FN(double, "%f") JC_TEST_PRINT_TYPE_FN(int, "%d") JC_TEST_PRINT_TYPE_FN(unsigned int, "%u") JC_TEST_PRINT_TYPE_FN(char*, "%s") template <> char* jc_test_print_value(char* buffer, size_t buffer_len, const float value) { return buffer + JC_TEST_SNPRINTF(buffer, buffer_len, "%f", JC_TEST_STATIC_CAST(double, value)); } #define JC_TEST_CLR_DEFAULT "\x1B[0m" #define JC_TEST_CLR_RED "\x1B[31m" #define JC_TEST_CLR_GREEN "\x1B[32m" #define JC_TEST_CLR_YELLOW "\x1B[33m" #define JC_TEST_CLR_MAGENTA "\x1B[35m" #define JC_TEST_CLR_CYAN "\x1B[36m" #define JC_TEST_COL(CLR) (jc_test_get_state()->is_a_tty ? JC_TEST_CLR_ ## CLR : "") static size_t jc_test_snprint_time(char* buffer, size_t buffer_len, jc_test_time_t t); #if defined(__GNUC__) || defined(__clang__) __attribute__ ((format (printf, 5, 6))) #endif void jc_test_logf(const jc_test_fixture* fixture, const jc_test_entry* test, const jc_test_stats* stats, int event, const char* format, ...) { char buffer[1024]; char* cursor = buffer; const char* end = buffer + sizeof(buffer); if (event == JC_TEST_EVENT_FIXTURE_SETUP) { JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "%s%s%s\n", JC_TEST_COL(CYAN), fixture->name, JC_TEST_COL(DEFAULT)); } else if (event == JC_TEST_EVENT_FIXTURE_TEARDOWN) { jc_test_time_t totaltime = fixture->stats.totaltime; if (fixture->parent) { totaltime = fixture->parent->stats.totaltime; } cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "%s%s%s took ", JC_TEST_COL(CYAN), fixture->name, JC_TEST_COL(DEFAULT)); cursor += jc_test_snprint_time(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), totaltime); JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "\n"); } else if (event == JC_TEST_EVENT_TEST_SETUP) { cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "%s%s%s", JC_TEST_COL(YELLOW), test->name, JC_TEST_COL(DEFAULT)); if (fixture->index != 0xFFFFFFFF) { JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "/%d ", fixture->index); } } else if (event == JC_TEST_EVENT_TEST_TEARDOWN) { const char* pass = jc_test_get_state()->is_a_tty ? JC_TEST_CLR_GREEN "PASS" JC_TEST_CLR_DEFAULT : "PASS"; const char* fail = jc_test_get_state()->is_a_tty ? JC_TEST_CLR_RED "FAIL" JC_TEST_CLR_DEFAULT : "FAIL"; const char* skipped = jc_test_get_state()->is_a_tty ? JC_TEST_CLR_MAGENTA "SKIPPED" JC_TEST_CLR_DEFAULT : "SKIPPED"; cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "\n%s%s%s", JC_TEST_COL(YELLOW), test->name, JC_TEST_COL(DEFAULT)); if (fixture->index != 0xFFFFFFFF) { cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "/%d ", fixture->index); } cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), " %s (", test->fail ? fail : (test->skipped ? skipped : pass)); cursor += jc_test_snprint_time(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), stats->totaltime); JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), ")\n"); } else if (event == JC_TEST_EVENT_ASSERT_FAILED) { cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "\n%s%s%s:%d:", JC_TEST_COL(MAGENTA), fixture->filename, JC_TEST_COL(DEFAULT), fixture->line); if (format) { va_list ap; va_start(ap, format); vsnprintf(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), format, ap); va_end(ap); } } else if (event == JC_TEST_EVENT_SUMMARY) { // print failed tests fixture = jc_test_get_state()->fixtures; while (stats->num_fail && fixture) { if (!fixture->fail) { test = fixture->tests; while(test) { if (test->fail) { cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "%s%s%s.%s%s %sfailed%s\n", JC_TEST_COL(MAGENTA), fixture->name, JC_TEST_COL(DEFAULT), JC_TEST_COL(YELLOW), test->name, JC_TEST_COL(RED), JC_TEST_COL(DEFAULT)); } test = test->next; } } fixture = fixture->next; } cursor += JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "Ran %d tests, with %d assertions in ", stats->num_tests, stats->num_assertions); cursor += jc_test_snprint_time(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), stats->totaltime); if( stats->num_fail) JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "\n%d tests passed, %d skipped and %d tests %sFAILED%s\n", stats->num_pass, stats->num_skipped, stats->num_fail, JC_TEST_COL(RED), JC_TEST_COL(DEFAULT)); else JC_TEST_SNPRINTF(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), "\n%d tests %sPASSED%s and %d skipped\n", stats->num_pass, JC_TEST_COL(GREEN), JC_TEST_COL(DEFAULT), stats->num_skipped); } else if (event == JC_TEST_EVENT_GENERIC) { if (format) { va_list ap; va_start(ap, format); vsnprintf(cursor, JC_TEST_STATIC_CAST(size_t,end-cursor), format, ap); va_end(ap); } } buffer[sizeof(buffer)-1] = 0; printf("%s", buffer); } #undef JC_TEST_COL static inline void jc_test_memset(void* _mem, unsigned int pattern, size_t size) { for (size_t i = 0; i < size; ++i) { JC_TEST_CAST(unsigned char*, _mem)[i] = JC_TEST_STATIC_CAST(unsigned char, pattern & 0xFF); } } void jc_test_memcpy(void* dst, void* src, size_t size) { for (size_t i = 0; i < size; ++i) { JC_TEST_CAST(unsigned char*, dst)[i] = JC_TEST_CAST(unsigned char*, src)[i]; } } int jc_test_streq(const char* a, const char* b) { if (a == 0) return (b == 0) ? 1 : 0; if (b == 0) return 0; while (*a && (*a == *b)) { ++a; ++b; } return (*a - *b) == 0 ? 1 : 0; } static inline int jc_test_compare_str(const char* a, const char* b) { while(*a && *b) { if (*a != *b) return 0; ++a; ++b; } return *b == 0; } static const char* jc_test_strstr(const char* a, const char* b) { while (*a) { if (*a == *b && jc_test_compare_str(a, b)) return a; ++a; } return 0; } int jc_test_cmp_NEAR(double a, double b, double epsilon, const char* exprA, const char* exprB, const char* exprC) { double diff = a > b ? a - b : b - a; if (diff <= epsilon) return 1; char bA[64]; jc_test_print_value(bA, sizeof(bA), a); char bB[64]; jc_test_print_value(bB, sizeof(bB), b); char bEpsilon[64]; jc_test_print_value(bEpsilon, sizeof(bEpsilon), epsilon); char bDiff[64]; jc_test_print_value(bDiff, sizeof(bDiff), diff); JC_TEST_LOGF(jc_test_get_fixture(), jc_test_get_test(), 0, JC_TEST_EVENT_ASSERT_FAILED, "\nValue of: abs(%s - %s) <= %s\nExpected: abs(%s - %s) <= %s\n Actual: abs(%s - %s) == %s\n", exprA, exprB, exprC, bA, bB, bEpsilon, bA, bB, bDiff); return 0; } int jc_test_cmp_STREQ(const char* a, const char* b, const char* exprA, const char* exprB) { if (jc_test_streq(a, b)) return 1; jc_test_log_failure_str(a, b, exprA, exprB, "=="); return 0; } int jc_test_cmp_STRNE(const char* a, const char* b, const char* exprA, const char* exprB) { if (!jc_test_streq(a, b)) return 1; jc_test_log_failure_str(a, b, exprA, exprB, "!="); return 0; } // http://en.wikipedia.org/wiki/Signed_number_representations template <typename IntType> static inline IntType jc_test_float_to_biased(IntType bits) { const IntType sign_bit = JC_TEST_STATIC_CAST(IntType, 1) << (8*sizeof(IntType) - 1); return (sign_bit & bits) ? ~bits + 1 : bits | sign_bit; } template <typename FloatType, typename IntType> static int jc_test_cmp_float_almost_equal(FloatType a, FloatType b) { static const int max_ulp = 4; union { FloatType f; IntType i; } ua, ub; ua.f = a; ub.f = b; IntType biased_a = jc_test_float_to_biased<IntType>(ua.i); IntType biased_b = jc_test_float_to_biased<IntType>(ub.i); IntType dist_ulp = (biased_a > biased_b) ? (biased_a - biased_b) : (biased_b - biased_a); return dist_ulp <= max_ulp; } int jc_test_cmp_double_eq(double a, double b) { return jc_test_cmp_float_almost_equal<double, JC_TEST_UINT64>(a, b); } int jc_test_cmp_float_eq(float a, float b) { return jc_test_cmp_float_almost_equal<float, JC_TEST_UINT32>(a, b); } jc_test_factory_base_interface::~jc_test_factory_base_interface() {} jc_test_fixture::~jc_test_fixture() {} void jc_test_fixture::SetParam() {} jc_test_base_class::~jc_test_base_class() {} void jc_test_base_class::SetUp() {} void jc_test_base_class::TearDown() {} jc_test_fixture* jc_test_create_fixture(jc_test_fixture* fixture, const char* name, unsigned int fixture_type) { fixture->next = 0; fixture->tests = 0; fixture->name = name; fixture->type = fixture_type; fixture->parent = 0; fixture->fail = 0; fixture->fatal = 0; fixture->index = 0xFFFFFFFF; fixture->num_tests = 0; fixture->first = fixture->last = 1; fixture->signum = 0; fixture->fixture_setup = 0; fixture->fixture_teardown = 0; jc_test_memset(&fixture->stats, 0, sizeof(fixture->stats)); jc_test_get_state()->num_fixtures++; jc_test_fixture* prev = jc_test_get_state()->fixtures; if (!prev) jc_test_get_state()->fixtures = fixture; else { while (prev->next) prev = prev->next; prev->next = fixture; } return fixture; } jc_test_entry* jc_test_add_test_to_fixture(jc_test_fixture* fixture, const char* test_name, jc_test_base_class* instance, jc_test_factory_base_interface* factory) { jc_test_entry* test = new jc_test_entry; test->next = 0; test->name = test_name; test->instance = instance; test->factory = factory; test->fail = 0; test->skipped = 0; jc_test_entry* prev = fixture->tests; if (!prev) fixture->tests = test; else { while(prev->next) prev = prev->next; prev->next = test; } fixture->num_tests++; return test; } jc_test_fixture* jc_test_find_fixture(const char* name, unsigned int fixture_type) { jc_test_fixture* fixture = jc_test_get_state()->fixtures; while (fixture) { if (fixture->type == fixture_type && jc_test_streq(fixture->name, name)) return fixture; fixture = fixture->next; } return 0; } jc_test_fixture* jc_test_alloc_fixture(const char* name, unsigned int fixture_type) { return jc_test_create_fixture(new jc_test_fixture, name, fixture_type); } int jc_test_register_class_test(const char* fixture_name, const char* test_name, jc_test_void_staticfunc class_setup, jc_test_void_staticfunc class_teardown, jc_test_base_class* instance, unsigned int fixture_type) { jc_test_fixture* fixture = jc_test_find_fixture(fixture_name, fixture_type); if (!fixture) { fixture = jc_test_alloc_fixture(fixture_name, fixture_type); fixture->fixture_setup = class_setup; fixture->fixture_teardown = class_teardown; } jc_test_add_test_to_fixture(fixture, test_name, instance, 0); return 0; } void jc_test_exit() { jc_test_state* state = jc_test_get_state(); jc_test_fixture* fixture = state->fixtures; while (fixture) { jc_test_entry* test = fixture->tests; while (test) { delete test->instance; delete test->factory; jc_test_entry* tmp_test = test; test = test->next; delete tmp_test; } jc_test_fixture* tmp_fixture = fixture; fixture = fixture->next; delete tmp_fixture; } } void jc_test_set_test_fail(int fatal) { jc_test_get_test()->fail = 1; jc_test_get_fixture()->fail = 1; jc_test_get_fixture()->fatal |= fatal; } void jc_test_set_test_skipped() { jc_test_get_test()->skipped = 1; } void jc_test_increment_assertions() { jc_test_get_fixture()->stats.num_assertions++; } static size_t jc_test_snprint_time(char* buffer, size_t buffer_len, jc_test_time_t t) { // Micro seconds #ifdef _MSC_VER #define JC_TEST_MICROSECONDS_STR "us" #else #define JC_TEST_MICROSECONDS_STR "\u00b5s" #endif int printed; if( t < 5000 ) printed = JC_TEST_SNPRINTF(buffer, buffer_len, "%g %s", JC_TEST_STATIC_CAST(double, t), JC_TEST_MICROSECONDS_STR); else if( t < 500000 ) printed = JC_TEST_SNPRINTF(buffer, buffer_len, "%g %s", t / 1000.0, "ms"); else printed = JC_TEST_SNPRINTF(buffer, buffer_len, "%g %s", t / 1000000.0, "s"); return JC_TEST_STATIC_CAST(size_t, printed); } #define JC_TEST_INVOKE_MEMBER_FN(INSTANCE, FN) \ (JC_TEST_CAST(jc_test_base_class*,INSTANCE) ->* JC_TEST_CAST(jc_test_void_memberfunc,FN)) () static void jc_test_run_fixture(jc_test_fixture* fixture) { jc_test_get_state()->current_fixture = fixture; if (fixture->type == JC_TEST_FIXTURE_TYPE_PARAMS_CLASS) { return; } jc_test_memset(&fixture->stats, 0, sizeof(fixture->stats)); fixture->stats.totaltime = 0; jc_test_time_t timestart = JC_TEST_TIMING_FUNC(); if (fixture->first) { JC_TEST_LOGF(fixture, 0, 0, JC_TEST_EVENT_FIXTURE_SETUP, 0); } if (fixture->first && fixture->fixture_setup != 0) { fixture->fixture_setup(); } jc_test_entry* test = fixture->tests; while (test) { fixture->fail = 0; test->fail = 0; if (!test->skipped) { jc_test_get_state()->current_test = test; fixture->SetParam(); JC_TEST_LOGF(fixture, test, 0, JC_TEST_EVENT_TEST_SETUP, 0); jc_test_time_t teststart = 0; jc_test_time_t testend = 0; jc_test_void_memberfunc cppfns[3] = { &jc_test_base_class::SetUp, &jc_test_base_class::TestBody, &jc_test_base_class::TearDown }; for( int i = 0; i < 3; ++i ) { if( i == 1 ) { teststart = JC_TEST_TIMING_FUNC(); } JC_TEST_INVOKE_MEMBER_FN(test->instance, cppfns[i]); if( i == 1 ) { testend = JC_TEST_TIMING_FUNC(); } if( fixture->fatal ) { break; } } jc_test_stats test_stats = {0, 0, 0, 0, 0, testend-teststart}; JC_TEST_LOGF(fixture, test, &test_stats, JC_TEST_EVENT_TEST_TEARDOWN, 0); } if( test->fail ) ++fixture->stats.num_fail; else if ( test->skipped ) ++fixture->stats.num_skipped; else ++fixture->stats.num_pass; ++fixture->stats.num_tests; test = test->next; } jc_test_get_state()->current_test = 0; if (fixture->last && fixture->fixture_teardown != 0) { fixture->fixture_teardown(); } jc_test_time_t timeend = JC_TEST_TIMING_FUNC(); fixture->stats.totaltime = timeend - timestart; if (fixture->parent) { fixture->parent->stats.totaltime += fixture->stats.totaltime; } if (fixture->last) { JC_TEST_LOGF(fixture, 0, &fixture->stats, JC_TEST_EVENT_FIXTURE_TEARDOWN, 0); } jc_test_get_state()->current_fixture = 0; } #if defined(_WIN32) #include <Windows.h> jc_test_time_t jc_test_get_time(void) { LARGE_INTEGER tickPerSecond; LARGE_INTEGER tick; QueryPerformanceFrequency(&tickPerSecond); QueryPerformanceCounter(&tick); return JC_TEST_STATIC_CAST(jc_test_time_t, tick.QuadPart / (tickPerSecond.QuadPart / 1000000)); } #else #include <sys/time.h> jc_test_time_t jc_test_get_time(void) { struct timeval tv; gettimeofday(&tv, 0); return JC_TEST_STATIC_CAST(jc_test_time_t, tv.tv_sec) * 1000000U + JC_TEST_STATIC_CAST(jc_test_time_t, tv.tv_usec); } #endif #if !defined(JC_TEST_NO_DEATH_TEST) #if defined(__clang__) || defined(__GNUC__) __attribute__ ((noreturn)) #endif static void jc_test_signal_handler(int) { longjmp(jc_test_get_state()->jumpenv, 1); } #if defined(_WIN32) || defined(__CYGWIN__) typedef void (*jc_test_signal_handler_fn)(int); jc_test_signal_handler_fn g_signal_handlers[4]; void jc_test_set_signal_handler() { g_signal_handlers[0] = signal(SIGILL, jc_test_signal_handler); g_signal_handlers[1] = signal(SIGABRT, jc_test_signal_handler); g_signal_handlers[2] = signal(SIGFPE, jc_test_signal_handler); g_signal_handlers[3] = signal(SIGSEGV, jc_test_signal_handler); } void jc_test_unset_signal_handler() { signal(SIGILL, g_signal_handlers[0]); signal(SIGABRT, g_signal_handlers[1]); signal(SIGFPE, g_signal_handlers[2]); signal(SIGSEGV, g_signal_handlers[3]); } #else static struct sigaction g_signal_handlers[6]; void jc_test_set_signal_handler() { #if !defined(_MSC_VER) && defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdisabled-macro-expansion" #endif struct sigaction handler; jc_test_memset(&handler, 0, sizeof(struct sigaction)); handler.sa_handler = jc_test_signal_handler; sigaction(SIGILL, &handler, &g_signal_handlers[0]); sigaction(SIGABRT, &handler, &g_signal_handlers[1]); sigaction(SIGBUS, &handler, &g_signal_handlers[2]); sigaction(SIGFPE, &handler, &g_signal_handlers[3]); sigaction(SIGSEGV, &handler, &g_signal_handlers[4]); sigaction(SIGPIPE, &handler, &g_signal_handlers[5]); #if !defined(_MSC_VER) && defined(__clang__) #pragma GCC diagnostic pop #endif } void jc_test_unset_signal_handler() { sigaction(SIGILL, &g_signal_handlers[0], 0); sigaction(SIGABRT, &g_signal_handlers[1], 0); sigaction(SIGBUS, &g_signal_handlers[2], 0); sigaction(SIGFPE, &g_signal_handlers[3], 0); sigaction(SIGSEGV, &g_signal_handlers[4], 0); sigaction(SIGPIPE, &g_signal_handlers[5], 0); } #endif #endif static jc_test_state* g_test_global_state = 0; jc_test_state* jc_test_get_state() { if (!g_test_global_state) { g_test_global_state = new jc_test_state; jc_test_memset(g_test_global_state, 0, sizeof(jc_test_state)); } return g_test_global_state; } static void jc_test_usage() { JC_TEST_LOGF(0, 0, 0, JC_TEST_EVENT_GENERIC, "jc_test options:\n"); JC_TEST_LOGF(0, 0, 0, JC_TEST_EVENT_GENERIC, "\t--test-filter <pattern> (e.g. --test-filter MathFuncs.Multiply/1)\n"); } static void jc_test_disable_tests(jc_test_state* state, const char* pattern) { jc_test_fixture* fixture = state->fixtures; while (fixture) { jc_test_entry* test = fixture->tests; while (test) { char name_buffer[256]; if (fixture->index >= 0xFFFFFFFF) JC_TEST_SNPRINTF(name_buffer, sizeof(name_buffer), "%s.%s/%d", fixture->name, test->name, fixture->index); else JC_TEST_SNPRINTF(name_buffer, sizeof(name_buffer), "%s.%s", fixture->name, test->name); if (jc_test_strstr(name_buffer, pattern) == 0) test->skipped = 1; test = test->next; } fixture = fixture->next; } } // checks for jctest specific command line arguments: e.g. "--test-filter Foo" static int jc_test_parse_commandline(int* argc, char** argv) { int count=0; for (int i = 0; i < *argc; ++i) { const char* arg = argv[i]; if (jc_test_streq(arg, "--test-filter")) { if (i+1>=*argc) return 1; const char* pattern = argv[i+1]; for(int j = i+2; j < *argc; ++j) { argv[j-2] = argv[j]; } *argc -= 2; jc_test_disable_tests(jc_test_get_state(), pattern); } else { ++count; } } return 0; } int jc_test_run_all() { jc_test_state* state = jc_test_get_state(); state->stats.totaltime = 0; jc_test_fixture* fixture = state->fixtures; while (fixture) { jc_test_run_fixture( fixture ); state->stats.num_assertions += fixture->stats.num_assertions; state->stats.num_pass += fixture->stats.num_pass; state->stats.num_fail += fixture->stats.num_fail; state->stats.num_skipped += fixture->stats.num_skipped; state->stats.num_tests += fixture->stats.num_tests; state->stats.totaltime += fixture->stats.totaltime; fixture = fixture->next; } JC_TEST_LOGF(0, 0, &state->stats, JC_TEST_EVENT_SUMMARY, 0); int num_fail = state->stats.num_fail; jc_test_exit(); return num_fail; } #if defined(JC_TEST_NO_COLORS) #define JC_TEST_ISATTY(_X) 0U #else #ifndef JC_TEST_ISATTY #if defined(__CYGWIN__) || !defined(_WIN32) #include <unistd.h> // isatty #else // _WIN32 #include <io.h> // _isatty #include <string.h> #include <Windows.h> #endif #if !defined(_WIN32) #define JC_TEST_ISATTY(_X) isatty(_X) ? 1U : 0U #else #define JC_TEST_ISATTY(_X) (_isatty(_X) || jctest_cyg_isatty(_X)) ? 1U : 0U #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif // credit: https://github.com/ggreer/the_silver_searcher/pull/1146/files // Cygwin/msys2 pty is a pipe with the following format (H: hex-digit, N: 0-9): // '\{cygwin,msys}-HHHHHHHHHHHHHHHH-ptyN-{from,to}-master' static int jctest_cyg_isatty(int fd) { // The API here needs Vista or later SDK (WINVER 0x0600 or higher), and the // binary won't run on XP - unless NO_CYGTTY is defined (which disables it). // It's not impossible to make it work on XP, but not worth jumping through // the hoops, especially after msys2 and Cygwin dropped XP support in 2016. #if WINVER < 0x0600 (void)fd; return 0; #else HANDLE h = (HANDLE)_get_osfhandle(fd); if ((h == INVALID_HANDLE_VALUE) || (GetFileType(h) != FILE_TYPE_PIPE)) return 0; #define INFOSIZE (sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH) char buf[INFOSIZE + sizeof(WCHAR)]; // +1 WCHAR for our '\0' if (!GetFileInformationByHandleEx(h, FileNameInfo, buf, INFOSIZE)) return 0; FILE_NAME_INFO *info = (FILE_NAME_INFO *)buf; WCHAR *n = info->FileName; // no \0 from the API. We reserved extra char. n[info->FileNameLength / sizeof(WCHAR)] = 0; return ((wcsstr(n, L"\\msys-") == n) || (wcsstr(n, L"\\cygwin-") == n)) && wcsstr(n, L"-pty") && (wcsstr(n, L"-from-master") || wcsstr(n, L"-to-master")); #endif // WINVER } #endif // _WIN32 #endif // JC_TEST_ISATTY #endif // JC_TEST_NO_COLORS void jc_test_init(int* argc, char** argv) { if (jc_test_parse_commandline(argc, argv)) { jc_test_usage(); JC_TEST_EXIT(1); } jc_test_get_state()->is_a_tty = JC_TEST_ISATTY(1); #if !defined(JC_TEST_NO_COLORS) #if defined(_WIN32) // Try enabling ANSI escape sequence support on Windows 10 terminals. if (jc_test_get_state()->is_a_tty) { DWORD mode; HANDLE console_ = GetStdHandle(STD_OUTPUT_HANDLE); if (GetConsoleMode(console_, &mode)) { SetConsoleMode(console_, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); } } #endif #endif // JC_TEST_NO_COLORS } #if !defined(_MSC_VER) #pragma GCC diagnostic pop #endif #endif /* // Use case 2: // Test fixtures are good if you wish to call code before or after the test or fixture itself. struct MyTest : public jc_test_base_class { static void SetUpTestCase() {...}; static void TearDownTestCase() {...}; virtual void SetUp() {...}; virtual void TearDown() {...}; }; TEST_F(MyTest, TestName) { ASSERT_EQ(4, 2*2); } // Use case 3: // Parameterized tests are good if you wish to call a test case with different parameters struct MyParamTest : public jc_test_params_class<ParamType> { static void SetUpTestCase() {...}; static void TearDownTestCase() {...}; virtual void SetUp() {...}; virtual void TearDown() {...}; static const ParamType& GetParam(); // New param for each test iteration }; TEST_P(MyParamTest, IsEven) { ParamType value = GetParam(); ASSERT_EQ(0, value & 1); } // Creates a new fixture for each test param INSTANTIATE_TEST_CASE_P(EvenValues, MyParamTest, jc_test_values(2,4,6,8,10)); */ char* foo; TEST(test, x) { ASSERT_TRUE(foo); }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
About the author
Statistics
Changelog
Version tree