├── .editorconfig ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── neon_sim.cpp ├── neon_sim.h └── tests ├── neon_helper.hpp ├── test_array.cpp ├── test_compare.cpp ├── test_util.cpp ├── test_util.hpp ├── test_vadd.cpp ├── test_vaddhn.cpp ├── test_vaddl.cpp ├── test_vaddw.cpp ├── test_vand.cpp ├── test_vext.cpp ├── test_vget.cpp ├── test_vhsub.cpp ├── test_vld.cpp ├── test_vmax_vmin.cpp ├── test_vmla.cpp ├── test_vmov.cpp ├── test_vmul.cpp ├── test_vqsub.cpp ├── test_vrecpe.cpp ├── test_vrev.cpp ├── test_vrsubhn.cpp ├── test_vst.cpp ├── test_vsub.cpp ├── test_vsubhn.cpp ├── test_vsubl.cpp ├── test_vsubw.cpp ├── test_vtbl.cpp ├── test_vtrn.cpp ├── test_vuzp.cpp ├── test_vzip.cpp └── utest.h /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | ##trim_trailing_whitespace = true 9 | ##insert_final_newline = true 10 | 11 | [*.{h,hpp,c,cpp}] 12 | indent_size = 4 13 | 14 | [{stb_image,stb_image_write}.h] 15 | indent_size = 3 16 | 17 | [{CMakeLists.*,*.cmake}] 18 | indent_size = 2 19 | 20 | [{Makefile,makefile,*.mk}] 21 | indent_style = tab 22 | 23 | [*.{bat,cmd,cmd.*}] 24 | end_of_line = crlf 25 | indent_size = 2 26 | 27 | [*.{ps1,ps1.*}] 28 | end_of_line = crlf 29 | indent_size = 4 30 | 31 | [*.{md,markdown}] 32 | indent_size = 4 33 | 34 | [*.{yml,yaml}] 35 | indent_size = 2 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | **/.cache/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(neon_sim) 3 | 4 | option(NEON_SIM_BUILD_TEST "build test?" OFF) 5 | 6 | set(CMAKE_CXX_STANDARD 11) 7 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 8 | 9 | add_library(neon_sim STATIC neon_sim.cpp) 10 | target_include_directories(neon_sim PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 11 | 12 | if(NEON_SIM_BUILD_TEST) 13 | enable_testing() 14 | file(GLOB_RECURSE TEST_SOURCES "tests/*.cpp") 15 | add_executable(test_neon_sim ${TEST_SOURCES}) 16 | target_link_libraries(test_neon_sim PRIVATE neon_sim) 17 | endif() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-Present Zhuo Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # neon_sim 2 | 3 | ARM NEON Intrinsics implementation in C, for accurate understanding of each "neon function". 4 | 5 | ## Usage 6 | 7 | Replace 8 | ```cpp 9 | #if __ARM_NEON 10 | #include 11 | #endif // __ARM_NEON 12 | ``` 13 | with 14 | ```cpp 15 | #include "neon_sim.h" 16 | ``` 17 | 18 | 19 | ## Features 20 | 21 | - Real cross-platform 22 | - 100% in C/C++, no SSE stuffs 23 | - "Debuggable inside the intrinsics" 24 | - Closer to native neon usage 25 | - Support initializer list 26 | ```cpp 27 | int8x8_t a = { 1, -2, 3, 4, 5, 6, 7, 8 }; 28 | ``` 29 | - Support compile time vector type checking 30 | ```cpp 31 | uint8_t b[4] = { 5, 6, 7, 8 }; 32 | uint16x4_t v_b = vld1_u16(b); 33 | ``` 34 | - Support same-length-different-type conversion(require `-flax-vector-conversions` sometimes) 35 | 36 | ## Known issues 37 | 38 | 1. The correctness of the simulation implementation is not guaranteed. 39 | 2. However, the accuracy can be improved by adding examples and continuously verifying. 40 | 3. Not support inline assembly, i.e. `asm volatile("...")` stuffs. 41 | 4. `vgetq_lane_f32(sum_vec, 4)` failed to check-and-report index out of bounds of `[0, 3]` in compile time. 42 | -------------------------------------------------------------------------------- /tests/neon_helper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // neon specific ones 6 | #include 7 | 8 | // u8 9 | static std::ostream& operator<<(std::ostream& os, const uint8x8_t& v_data) 10 | { 11 | for (int i = 0; i < 8; i++) 12 | { 13 | if (i > 0) os << ", "; 14 | os << (int)v_data[i]; 15 | } 16 | return os; 17 | } 18 | 19 | static std::ostream& operator<<(std::ostream& os, const uint8x16_t& v_data) 20 | { 21 | for (int i = 0; i < 16; i++) 22 | { 23 | if (i > 0) os << ", "; 24 | os << (int)v_data[i]; 25 | } 26 | return os; 27 | } 28 | 29 | // s8 30 | static std::ostream& operator<<(std::ostream& os, const int8x8_t& v_data) 31 | { 32 | for (int i = 0; i < 8; i++) 33 | { 34 | if (i > 0) os << ", "; 35 | os << (int)v_data[i]; 36 | } 37 | return os; 38 | } 39 | 40 | static std::ostream& operator<<(std::ostream& os, const int8x16_t& v_data) 41 | { 42 | for (int i = 0; i < 16; i++) 43 | { 44 | if (i > 0) os << ", "; 45 | os << (int)v_data[i]; 46 | } 47 | return os; 48 | } 49 | 50 | //u16 51 | static std::ostream& operator<<(std::ostream& os, const uint16x4_t& v_data) 52 | { 53 | for (int i = 0; i < 4; i++) 54 | { 55 | if (i > 0) os << ", "; 56 | os << (int)v_data[i]; 57 | } 58 | return os; 59 | } 60 | 61 | static std::ostream& operator<<(std::ostream& os, const uint16x8_t& v_data) 62 | { 63 | for (int i = 0; i < 8; i++) 64 | { 65 | if (i > 0) os << ", "; 66 | os << (int)v_data[i]; 67 | } 68 | return os; 69 | } 70 | 71 | // s16 72 | static std::ostream& operator<<(std::ostream& os, const int16x4_t& v_data) 73 | { 74 | for (int i = 0; i < 4; i++) 75 | { 76 | if (i > 0) os << ", "; 77 | os << v_data[i]; 78 | } 79 | return os; 80 | } 81 | 82 | static std::ostream& operator<<(std::ostream& os, const int16x8_t& v_data) 83 | { 84 | for (int i = 0; i < 8; i++) 85 | { 86 | if (i > 0) os << ", "; 87 | os << v_data[i]; 88 | } 89 | return os; 90 | } 91 | 92 | // u32 93 | static std::ostream& operator<<(std::ostream& os, const uint32x2_t& v_data) 94 | { 95 | for (int i = 0; i < 2; i++) 96 | { 97 | if (i > 0) os << ", "; 98 | os << v_data[i]; 99 | } 100 | return os; 101 | } 102 | 103 | static std::ostream& operator<<(std::ostream& os, const uint32x4_t& v_data) 104 | { 105 | for (int i = 0; i < 4; i++) 106 | { 107 | if (i > 0) os << ", "; 108 | os << v_data[i]; 109 | } 110 | return os; 111 | } 112 | 113 | // s32 114 | static std::ostream& operator<<(std::ostream& os, const int32x2_t& v_data) 115 | { 116 | for (int i = 0; i < 2; i++) 117 | { 118 | if (i > 0) os << ", "; 119 | os << v_data[i]; 120 | } 121 | return os; 122 | } 123 | 124 | static std::ostream& operator<<(std::ostream& os, const int32x4_t& v_data) 125 | { 126 | for (int i = 0; i < 4; i++) 127 | { 128 | if (i > 0) os << ", "; 129 | os << v_data[i]; 130 | } 131 | return os; 132 | } 133 | 134 | // float32 135 | static std::ostream& operator<<(std::ostream& os, const float32x2_t& v_data) 136 | { 137 | for (int i = 0; i < 2; i++) 138 | { 139 | if (i > 0) os << ", "; 140 | os << v_data[i]; 141 | } 142 | return os; 143 | } 144 | 145 | static std::ostream& operator<<(std::ostream& os, const float32x4_t& v_data) 146 | { 147 | for (int i = 0; i < 4; i++) 148 | { 149 | if (i > 0) os << ", "; 150 | os << v_data[i]; 151 | } 152 | return os; 153 | } 154 | 155 | // uint64 156 | static std::ostream& operator<<(std::ostream& os, const uint64x1_t& v_data) 157 | { 158 | for (int i = 0; i < 1; i++) 159 | { 160 | if (i > 0) os << ", "; 161 | os << v_data[i]; 162 | } 163 | return os; 164 | } 165 | 166 | static std::ostream& operator<<(std::ostream& os, const uint64x2_t& v_data) 167 | { 168 | for (int i = 0; i < 2; i++) 169 | { 170 | if (i > 0) os << ", "; 171 | os << v_data[i]; 172 | } 173 | return os; 174 | } 175 | 176 | // int64 177 | static std::ostream& operator<<(std::ostream& os, const int64x1_t& v_data) 178 | { 179 | for (int i = 0; i < 1; i++) 180 | { 181 | if (i > 0) os << ", "; 182 | os << v_data[i]; 183 | } 184 | return os; 185 | } 186 | 187 | static std::ostream& operator<<(std::ostream& os, const int64x2_t& v_data) 188 | { 189 | for (int i = 0; i < 2; i++) 190 | { 191 | if (i > 0) os << ", "; 192 | os << v_data[i]; 193 | } 194 | return os; 195 | } 196 | 197 | // float64 198 | static std::ostream& operator<<(std::ostream& os, const float64x1_t& v_data) 199 | { 200 | for (int i = 0; i < 1; i++) 201 | { 202 | if (i > 0) os << ", "; 203 | os << v_data[i]; 204 | } 205 | return os; 206 | } 207 | 208 | static std::ostream& operator<<(std::ostream& os, const float64x2_t& v_data) 209 | { 210 | for (int i = 0; i < 2; i++) 211 | { 212 | if (i > 0) os << ", "; 213 | os << v_data[i]; 214 | } 215 | return os; 216 | } 217 | 218 | /////// print register arrays 219 | 220 | // u8 221 | static std::ostream& operator<<(std::ostream& os, const uint8x8x2_t& v_data) 222 | { 223 | return os << v_data.val[0] << std::endl 224 | << v_data.val[1]; 225 | } 226 | static std::ostream& operator<<(std::ostream& os, const uint8x8x3_t& v_data) 227 | { 228 | return os << v_data.val[0] << std::endl 229 | << v_data.val[1] << std::endl 230 | << v_data.val[2]; 231 | } 232 | static std::ostream& operator<<(std::ostream& os, const uint8x8x4_t& v_data) 233 | { 234 | return os << v_data.val[0] << std::endl 235 | << v_data.val[1] << std::endl 236 | << v_data.val[2] << std::endl 237 | << v_data.val[3]; 238 | } 239 | static std::ostream& operator<<(std::ostream& os, const uint8x16x2_t& v_data) 240 | { 241 | return os << v_data.val[0] << std::endl 242 | << v_data.val[1]; 243 | } 244 | static std::ostream& operator<<(std::ostream& os, const uint8x16x3_t& v_data) 245 | { 246 | return os << v_data.val[0] << std::endl 247 | << v_data.val[1] << std::endl 248 | << v_data.val[2]; 249 | } 250 | static std::ostream& operator<<(std::ostream& os, const uint8x16x4_t& v_data) 251 | { 252 | return os << v_data.val[0] << std::endl 253 | << v_data.val[1] << std::endl 254 | << v_data.val[2] << std::endl 255 | << v_data.val[3]; 256 | } 257 | 258 | // s8 259 | static std::ostream& operator<<(std::ostream& os, const int8x8x2_t& v_data) 260 | { 261 | return os << v_data.val[0] << std::endl 262 | << v_data.val[1]; 263 | } 264 | 265 | static std::ostream& operator<<(std::ostream& os, const int8x8x3_t& v_data) 266 | { 267 | return os << v_data.val[0] << std::endl 268 | << v_data.val[1] << std::endl 269 | << v_data.val[2]; 270 | } 271 | 272 | static std::ostream& operator<<(std::ostream& os, const int8x8x4_t& v_data) 273 | { 274 | return os << v_data.val[0] << std::endl 275 | << v_data.val[1] << std::endl 276 | << v_data.val[2] << std::endl 277 | << v_data.val[3]; 278 | } 279 | 280 | static std::ostream& operator<<(std::ostream& os, const int8x16x2_t& v_data) 281 | { 282 | return os << v_data.val[0] << std::endl 283 | << v_data.val[1]; 284 | } 285 | 286 | static std::ostream& operator<<(std::ostream& os, const int8x16x3_t& v_data) 287 | { 288 | return os << v_data.val[0] << std::endl 289 | << v_data.val[1] << std::endl 290 | << v_data.val[2]; 291 | } 292 | 293 | static std::ostream& operator<<(std::ostream& os, const int8x16x4_t& v_data) 294 | { 295 | return os << v_data.val[0] << std::endl 296 | << v_data.val[1] << std::endl 297 | << v_data.val[2] << std::endl 298 | << v_data.val[3]; 299 | } 300 | 301 | // u16 302 | static std::ostream& operator<<(std::ostream& os, const uint16x4x2_t& v_data) 303 | { 304 | return os << v_data.val[0] << std::endl 305 | << v_data.val[1]; 306 | } 307 | 308 | static std::ostream& operator<<(std::ostream& os, const uint16x4x3_t& v_data) 309 | { 310 | return os << v_data.val[0] << std::endl 311 | << v_data.val[1] << std::endl 312 | << v_data.val[2]; 313 | } 314 | 315 | static std::ostream& operator<<(std::ostream& os, const uint16x4x4_t& v_data) 316 | { 317 | return os << v_data.val[0] << std::endl 318 | << v_data.val[1] << std::endl 319 | << v_data.val[2] << std::endl 320 | << v_data.val[3]; 321 | } 322 | 323 | static std::ostream& operator<<(std::ostream& os, const uint16x8x2_t& v_data) 324 | { 325 | return os << v_data.val[0] << std::endl 326 | << v_data.val[1]; 327 | } 328 | 329 | static std::ostream& operator<<(std::ostream& os, const uint16x8x3_t& v_data) 330 | { 331 | return os << v_data.val[0] << std::endl 332 | << v_data.val[1] << std::endl 333 | << v_data.val[2]; 334 | } 335 | 336 | static std::ostream& operator<<(std::ostream& os, const uint16x8x4_t& v_data) 337 | { 338 | return os << v_data.val[0] << std::endl 339 | << v_data.val[1] << std::endl 340 | << v_data.val[2] << std::endl 341 | << v_data.val[3]; 342 | } 343 | 344 | // s16 345 | static std::ostream& operator<<(std::ostream& os, const int16x4x2_t& v_data) 346 | { 347 | return os << v_data.val[0] << std::endl 348 | << v_data.val[1]; 349 | } 350 | 351 | static std::ostream& operator<<(std::ostream& os, const int16x4x3_t& v_data) 352 | { 353 | return os << v_data.val[0] << std::endl 354 | << v_data.val[1] << std::endl 355 | << v_data.val[2]; 356 | } 357 | 358 | static std::ostream& operator<<(std::ostream& os, const int16x4x4_t& v_data) 359 | { 360 | return os << v_data.val[0] << std::endl 361 | << v_data.val[1] << std::endl 362 | << v_data.val[2] << std::endl 363 | << v_data.val[3]; 364 | } 365 | 366 | static std::ostream& operator<<(std::ostream& os, const int16x8x2_t& v_data) 367 | { 368 | return os << v_data.val[0] << std::endl 369 | << v_data.val[1]; 370 | } 371 | 372 | static std::ostream& operator<<(std::ostream& os, const int16x8x3_t& v_data) 373 | { 374 | return os << v_data.val[0] << std::endl 375 | << v_data.val[1] << std::endl 376 | << v_data.val[2]; 377 | } 378 | 379 | static std::ostream& operator<<(std::ostream& os, const int16x8x4_t& v_data) 380 | { 381 | return os << v_data.val[0] << std::endl 382 | << v_data.val[1] << std::endl 383 | << v_data.val[2] << std::endl 384 | << v_data.val[3]; 385 | } 386 | 387 | // u32 388 | static std::ostream& operator<<(std::ostream& os, const uint32x2x2_t& v_data) 389 | { 390 | return os << v_data.val[0] << std::endl 391 | << v_data.val[1]; 392 | } 393 | 394 | static std::ostream& operator<<(std::ostream& os, const uint32x2x3_t& v_data) 395 | { 396 | return os << v_data.val[0] << std::endl 397 | << v_data.val[1] << std::endl 398 | << v_data.val[2]; 399 | } 400 | 401 | static std::ostream& operator<<(std::ostream& os, const uint32x2x4_t& v_data) 402 | { 403 | return os << v_data.val[0] << std::endl 404 | << v_data.val[1] << std::endl 405 | << v_data.val[2] << std::endl 406 | << v_data.val[3]; 407 | } 408 | 409 | static std::ostream& operator<<(std::ostream& os, const uint32x4x2_t& v_data) 410 | { 411 | return os << v_data.val[0] << std::endl 412 | << v_data.val[1]; 413 | } 414 | 415 | static std::ostream& operator<<(std::ostream& os, const uint32x4x3_t& v_data) 416 | { 417 | return os << v_data.val[0] << std::endl 418 | << v_data.val[1] << std::endl 419 | << v_data.val[2]; 420 | } 421 | 422 | static std::ostream& operator<<(std::ostream& os, const uint32x4x4_t& v_data) 423 | { 424 | return os << v_data.val[0] << std::endl 425 | << v_data.val[1] << std::endl 426 | << v_data.val[2] << std::endl 427 | << v_data.val[3]; 428 | } 429 | 430 | // s32 431 | static std::ostream& operator<<(std::ostream& os, const int32x2x2_t& v_data) 432 | { 433 | return os << v_data.val[0] << std::endl 434 | << v_data.val[1]; 435 | } 436 | 437 | static std::ostream& operator<<(std::ostream& os, const int32x2x3_t& v_data) 438 | { 439 | return os << v_data.val[0] << std::endl 440 | << v_data.val[1] << std::endl 441 | << v_data.val[2]; 442 | } 443 | 444 | static std::ostream& operator<<(std::ostream& os, const int32x2x4_t& v_data) 445 | { 446 | return os << v_data.val[0] << std::endl 447 | << v_data.val[1] << std::endl 448 | << v_data.val[2] << std::endl 449 | << v_data.val[3]; 450 | } 451 | 452 | static std::ostream& operator<<(std::ostream& os, const int32x4x2_t& v_data) 453 | { 454 | return os << v_data.val[0] << std::endl 455 | << v_data.val[1]; 456 | } 457 | 458 | static std::ostream& operator<<(std::ostream& os, const int32x4x3_t& v_data) 459 | { 460 | return os << v_data.val[0] << std::endl 461 | << v_data.val[1] << std::endl 462 | << v_data.val[2]; 463 | } 464 | 465 | static std::ostream& operator<<(std::ostream& os, const int32x4x4_t& v_data) 466 | { 467 | return os << v_data.val[0] << std::endl 468 | << v_data.val[1] << std::endl 469 | << v_data.val[2] << std::endl 470 | << v_data.val[3]; 471 | } 472 | 473 | // f32 474 | static std::ostream& operator<<(std::ostream& os, const float32x2x2_t& v_data) 475 | { 476 | return os << v_data.val[0] << std::endl 477 | << v_data.val[1]; 478 | } 479 | 480 | static std::ostream& operator<<(std::ostream& os, const float32x2x3_t& v_data) 481 | { 482 | return os << v_data.val[0] << std::endl 483 | << v_data.val[1] << std::endl 484 | << v_data.val[2]; 485 | } 486 | 487 | static std::ostream& operator<<(std::ostream& os, const float32x2x4_t& v_data) 488 | { 489 | return os << v_data.val[0] << std::endl 490 | << v_data.val[1] << std::endl 491 | << v_data.val[2] << std::endl 492 | << v_data.val[3]; 493 | } 494 | 495 | static std::ostream& operator<<(std::ostream& os, const float32x4x2_t& v_data) 496 | { 497 | return os << v_data.val[0] << std::endl 498 | << v_data.val[1]; 499 | } 500 | 501 | static std::ostream& operator<<(std::ostream& os, const float32x4x3_t& v_data) 502 | { 503 | return os << v_data.val[0] << std::endl 504 | << v_data.val[1] << std::endl 505 | << v_data.val[2]; 506 | } 507 | 508 | static std::ostream& operator<<(std::ostream& os, const float32x4x4_t& v_data) 509 | { 510 | return os << v_data.val[0] << std::endl 511 | << v_data.val[1] << std::endl 512 | << v_data.val[2] << std::endl 513 | << v_data.val[3]; 514 | } 515 | 516 | // u64 517 | static std::ostream& operator<<(std::ostream& os, const uint64x1x2_t& v_data) 518 | { 519 | return os << v_data.val[0] << std::endl 520 | << v_data.val[1]; 521 | } 522 | 523 | static std::ostream& operator<<(std::ostream& os, const uint64x1x3_t& v_data) 524 | { 525 | return os << v_data.val[0] << std::endl 526 | << v_data.val[1] << std::endl 527 | << v_data.val[2]; 528 | } 529 | 530 | static std::ostream& operator<<(std::ostream& os, const uint64x1x4_t& v_data) 531 | { 532 | return os << v_data.val[0] << std::endl 533 | << v_data.val[1] << std::endl 534 | << v_data.val[2] << std::endl 535 | << v_data.val[3]; 536 | } 537 | 538 | static std::ostream& operator<<(std::ostream& os, const uint64x2x2_t& v_data) 539 | { 540 | return os << v_data.val[0] << std::endl 541 | << v_data.val[1]; 542 | } 543 | 544 | static std::ostream& operator<<(std::ostream& os, const uint64x2x3_t& v_data) 545 | { 546 | return os << v_data.val[0] << std::endl 547 | << v_data.val[1] << std::endl 548 | << v_data.val[2]; 549 | } 550 | 551 | static std::ostream& operator<<(std::ostream& os, const uint64x2x4_t& v_data) 552 | { 553 | return os << v_data.val[0] << std::endl 554 | << v_data.val[1] << std::endl 555 | << v_data.val[2] << std::endl 556 | << v_data.val[3]; 557 | } 558 | 559 | // s64 560 | static std::ostream& operator<<(std::ostream& os, const int64x1x2_t& v_data) 561 | { 562 | return os << v_data.val[0] << std::endl 563 | << v_data.val[1]; 564 | } 565 | 566 | static std::ostream& operator<<(std::ostream& os, const int64x1x3_t& v_data) 567 | { 568 | return os << v_data.val[0] << std::endl 569 | << v_data.val[1] << std::endl 570 | << v_data.val[2]; 571 | } 572 | 573 | static std::ostream& operator<<(std::ostream& os, const int64x1x4_t& v_data) 574 | { 575 | return os << v_data.val[0] << std::endl 576 | << v_data.val[1] << std::endl 577 | << v_data.val[2] << std::endl 578 | << v_data.val[3]; 579 | } 580 | 581 | static std::ostream& operator<<(std::ostream& os, const int64x2x2_t& v_data) 582 | { 583 | return os << v_data.val[0] << std::endl 584 | << v_data.val[1]; 585 | } 586 | 587 | static std::ostream& operator<<(std::ostream& os, const int64x2x3_t& v_data) 588 | { 589 | return os << v_data.val[0] << std::endl 590 | << v_data.val[1] << std::endl 591 | << v_data.val[2]; 592 | } 593 | 594 | static std::ostream& operator<<(std::ostream& os, const int64x2x4_t& v_data) 595 | { 596 | return os << v_data.val[0] << std::endl 597 | << v_data.val[1] << std::endl 598 | << v_data.val[2] << std::endl 599 | << v_data.val[3]; 600 | } 601 | 602 | // f64 603 | #if __aarch64__ 604 | static std::ostream& operator<<(std::ostream& os, const float64x1x2_t& v_data) 605 | { 606 | return os << v_data.val[0] << std::endl 607 | << v_data.val[1]; 608 | } 609 | 610 | static std::ostream& operator<<(std::ostream& os, const float64x1x3_t& v_data) 611 | { 612 | return os << v_data.val[0] << std::endl 613 | << v_data.val[1] << std::endl 614 | << v_data.val[2]; 615 | } 616 | 617 | static std::ostream& operator<<(std::ostream& os, const float64x1x4_t& v_data) 618 | { 619 | return os << v_data.val[0] << std::endl 620 | << v_data.val[1] << std::endl 621 | << v_data.val[2] << std::endl 622 | << v_data.val[3]; 623 | } 624 | 625 | static std::ostream& operator<<(std::ostream& os, const float64x2x2_t& v_data) 626 | { 627 | return os << v_data.val[0] << std::endl 628 | << v_data.val[1]; 629 | } 630 | 631 | static std::ostream& operator<<(std::ostream& os, const float64x2x3_t& v_data) 632 | { 633 | return os << v_data.val[0] << std::endl 634 | << v_data.val[1] << std::endl 635 | << v_data.val[2]; 636 | } 637 | 638 | static std::ostream& operator<<(std::ostream& os, const float64x2x4_t& v_data) 639 | { 640 | return os << v_data.val[0] << std::endl 641 | << v_data.val[1] << std::endl 642 | << v_data.val[2] << std::endl 643 | << v_data.val[3]; 644 | } 645 | #endif // __aarch64__ 646 | -------------------------------------------------------------------------------- /tests/test_array.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(array, lane) 4 | { 5 | float32x4_t v0 = { 1.0, 2.0, 3.0, 4.0 }; 6 | float32x4_t v1 = { 5.0, 6.0, 7.0, 8.0 }; 7 | float32x4_t v2 = { 9.0, 10.0, 11.0, 12.0 }; 8 | float32x4_t v3 = { 13.0, 14.0, 15.0, 16.0 }; 9 | float32x4x4_t ary = { v0, v1, v2, v3 }; 10 | float32x4_t actual = ary.val[2]; 11 | float32x4_t expected = { 9.0, 10.0, 11.0, 12.0 }; 12 | EXPECT_TRUE(almostEqual(expected, actual)); 13 | } 14 | -------------------------------------------------------------------------------- /tests/test_compare.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vcltq, f32) 4 | { 5 | float32x4_t v1 = { 1.0, 0.0, 1.0, 0.0 }; 6 | float32x4_t v2 = { 0.0, 1.0, 1.0, 0.0 }; 7 | uint32x4_t mask = vcltq_f32(v1, v2); // v1 < v2 8 | float32x4_t ones = vmovq_n_f32(10.0); 9 | float32x4_t twos = vmovq_n_f32(20.0); // the conditional branches: if condition is true returns 10.0, else returns 20.0 10 | float32x4_t actual = vbslq_f32(mask, ones, twos); // will select first if mask 0, second if mask 1 11 | float32x4_t expected = { 20.0, 10.0, 20.0, 20.0 }; 12 | EXPECT_TRUE(almostEqual(expected, actual)); 13 | } -------------------------------------------------------------------------------- /tests/test_util.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | UTEST_MAIN() -------------------------------------------------------------------------------- /tests/test_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //#include "gtest/gtest.h" 4 | #include "utest.h" 5 | #define TEST UTEST 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | //#include 13 | //#include "neon_helper.hpp" 14 | #include "neon_sim.h" 15 | 16 | 17 | static bool almostEqual(const uint8x8_t& expected, const uint8x8_t& actual, double eps=0) 18 | { 19 | for (int i = 0; i < 8; i++) 20 | { 21 | const double diff = std::fabs((double)expected.val[i] - (double)actual.val[i]); 22 | if (diff > eps) 23 | { 24 | std::cerr << "array[" << i << "] (" << (int)actual.val[i] << ") != expected[" << i << "] (" << (int)expected.val[i] 25 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 26 | return false; 27 | } 28 | } 29 | return true; 30 | } 31 | 32 | static bool almostEqual(const int8x8_t& expected, const int8x8_t& actual, double eps=0) 33 | { 34 | for (int i = 0; i < 8; i++) 35 | { 36 | const double diff = std::fabs((double)expected.val[i] - (double)actual.val[i]); 37 | if (diff > eps) 38 | { 39 | std::cerr << "array[" << i << "] (" << (int)actual.val[i] << ") != expected[" << i << "] (" << (int)expected.val[i] 40 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 41 | return false; 42 | } 43 | } 44 | return true; 45 | } 46 | 47 | static bool almostEqual(const uint16x8_t& expected, const uint16x8_t& actual, double eps=0) 48 | { 49 | for (int i = 0; i < 8; i++) 50 | { 51 | const double diff = std::fabs((double)expected.val[i] - (double)actual.val[i]); 52 | if (diff > eps) 53 | { 54 | std::cerr << "array[" << i << "] (" << (int)actual.val[i] << ") != expected[" << i << "] (" << (int)expected.val[i] 55 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 56 | return false; 57 | } 58 | } 59 | return true; 60 | } 61 | 62 | static bool almostEqual(const int16x8_t& expected, const int16x8_t& actual, double eps=0) 63 | { 64 | for (int i = 0; i < 8; i++) 65 | { 66 | const double diff = std::fabs((double)expected.val[i] - (double)actual.val[i]); 67 | if (diff > eps) 68 | { 69 | std::cerr << "array[" << i << "] (" << (int)actual.val[i] << ") != expected[" << i << "] (" << (int)expected.val[i] 70 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 71 | return false; 72 | } 73 | } 74 | return true; 75 | } 76 | 77 | static bool almostEqual(const int8x16_t& expected, const int8x16_t& actual, double eps=0) 78 | { 79 | for (int i = 0; i < 16; i++) 80 | { 81 | const double diff = std::fabs((double)expected.val[i] - (double)actual.val[i]); 82 | if (diff > eps) 83 | { 84 | std::cerr << "array[" << i << "] (" << (int)actual.val[i] << ") != expected[" << i << "] (" << (int)expected.val[i] 85 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 86 | return false; 87 | } 88 | } 89 | return true; 90 | } 91 | 92 | static bool almostEqual(const uint8x8x2_t& expected, const uint8x8x2_t& actual) 93 | { 94 | for (int i = 0; i < 2; i++) 95 | { 96 | if (!almostEqual(expected.val[i], actual.val[i])) 97 | { 98 | return false; 99 | } 100 | } 101 | return true; 102 | } 103 | 104 | static bool almostEqual(const float32x4_t& expected, const float32x4_t& actual, double eps=0) 105 | { 106 | for (int i = 0; i < 4; i++) 107 | { 108 | const double diff = std::fabs((double)expected.val[i] - (double)actual.val[i]); 109 | if (diff > eps) 110 | { 111 | std::cerr << "array[" << i << "] (" << (int)actual.val[i] << ") != expected[" << i << "] (" << (int)expected.val[i] 112 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 113 | return false; 114 | } 115 | } 116 | return true; 117 | } 118 | 119 | static bool almostEqual(const int16x4_t& expected, const int16x4_t& actual, double eps=0) 120 | { 121 | for (int i = 0; i < 4; i++) 122 | { 123 | const double diff = std::fabs((double)expected.val[i] - (double)actual.val[i]); 124 | if (diff > eps) 125 | { 126 | std::cerr << "array[" << i << "] (" << (int)actual.val[i] << ") != expected[" << i << "] (" << (int)expected.val[i] 127 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 128 | return false; 129 | } 130 | } 131 | return true; 132 | } 133 | 134 | static bool almostEqual(const std::vector& expected, const std::vector& actual, double eps=0) 135 | { 136 | for (int i = 0; i < 4; i++) 137 | { 138 | const double diff = std::fabs(expected[i] - actual[i]); 139 | if (diff > eps) 140 | { 141 | std::cerr << "array[" << i << "] (" << (int)actual[i] << ") != expected[" << i << "] (" << (int)expected[i] 142 | << "), diff = " << diff << ", EPS = " << eps << std::endl; 143 | return false; 144 | } 145 | } 146 | return true; 147 | } 148 | 149 | -------------------------------------------------------------------------------- /tests/test_vadd.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vaddq, f32) 4 | { 5 | float32x4_t v1 = { 1.0, 2.0, 3.0, 4.0 }; 6 | float32x4_t v2 = { 1.0, 1.0, 1.0, 1.0 }; 7 | float32x4_t actual = vaddq_f32(v1, v2); 8 | float32x4_t expected = { 2.0, 3.0, 4.0, 5.0 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | TEST(vadd, s8) 13 | { 14 | { 15 | int8x8_t v1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 16 | int8x8_t v2 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 17 | int8x8_t expected = { 2, 4, 6, 8, 10, 12, 14, 16 }; 18 | int8x8_t actual = vadd_s8(v1, v2); 19 | EXPECT_TRUE(almostEqual(expected, actual)); 20 | } 21 | 22 | { 23 | // 对应位元素相加: ri = ai + bi 24 | // 对于 signed 类型, 例如s8, 超过127后, 要减去256 从而得到正确结果 25 | int8x8_t v1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 26 | int8x8_t v2 = { 10, 20, 30, 40, 50, 60, 70, 127 }; 27 | int8x8_t actual = vadd_s8(v1, v2); 28 | int8x8_t expected = { 11, 22, 33, 44, 55, 66, 77, -121 }; 29 | EXPECT_TRUE(almostEqual(expected, actual)); 30 | } 31 | } 32 | 33 | TEST(vadd, s8_exceed) 34 | { 35 | int8x8_t v1 = { 120, 121, 122, 123, 124, 125, 126, 127 }; 36 | int8x8_t v2 = { 100, 100, 100, 100, 100, 100, 100, 100 }; 37 | 38 | // 并不是 {220, 221, 222, 234, 224, 225, 226, 227}, 而是分别减去256 39 | int8x8_t expected = { -36, -35, -34, -33, -32, -31, -30, -29 }; 40 | int8x8_t actual = vadd_s8(v1, v2); 41 | EXPECT_TRUE(almostEqual(expected, actual)); 42 | } 43 | 44 | TEST(vadd, u8) 45 | { 46 | uint8x8_t v1 = { 0, 1, 2, 3, 4, 5, 6, 7 }; 47 | uint8x8_t v2 = { 8, 9, 10, 11, 12, 13, 14, 15 }; 48 | uint8x8_t expected = { 8, 10, 12, 14, 16, 18, 20, 22 }; 49 | uint8x8_t actual = vadd_u8(v1, v2); 50 | EXPECT_TRUE(almostEqual(expected, actual)); 51 | } -------------------------------------------------------------------------------- /tests/test_vaddhn.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vaddhn_high_s16, normal) 4 | { 5 | int8x8_t r = { 0, 1, 2, 3, 4, 5, 6, 7 }; 6 | int16x8_t a = { 32000, 32001, 32002, 32003, 32004, 32005, 32006, 32007 }; 7 | int16x8_t b = { 1, 2, 3, 4, 5, 6, 7, 8 }; 8 | int8x16_t expected = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 125, 125, 125, 125, 125, 125, 125 }; 9 | int8x16_t actual = vaddhn_high_s16(r, a, b); 10 | EXPECT_TRUE(almostEqual(expected, actual)); 11 | } -------------------------------------------------------------------------------- /tests/test_vaddl.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vaddl, u8) 4 | { 5 | uint8x8_t v1 = { 240, 241, 242, 243, 244, 245, 246, 247 }; 6 | uint8x8_t v2 = { 100, 100, 100, 100, 100, 100, 100, 100 }; 7 | uint16x8_t expected = { 340, 341, 342, 343, 344, 345, 346, 347 }; 8 | uint16x8_t actual = vaddl_u8(v1, v2); 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | TEST(vaddl_high, s8) 13 | { 14 | int8x16_t v1 = { 0, 1, 2, 3, 4, 5, 6, 7, 100, 101, 102, 103, 104, 105, 106, 107 }; 15 | int8x16_t v2 = { 0, 1, 2, 3, 4, 5, 6, 7, 100, 100, 100, 100, 100, 100, 100, 100 }; 16 | int16x8_t expected = { 200, 201, 202, 203, 204, 205, 206, 207 }; 17 | int16x8_t actual = vaddl_high_s8(v1, v2); 18 | EXPECT_TRUE(almostEqual(expected, actual)); 19 | } -------------------------------------------------------------------------------- /tests/test_vaddw.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vaddw_high, s8) 4 | { 5 | int16x8_t v1 = { 0, 1, 2, 3, 4, 5, 6, 7 }; 6 | int8x16_t v2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; 7 | int16x8_t expected = { 8, 10, 12, 14, 16, 18, 20, 22 }; 8 | int16x8_t actual = vaddw_high_s8(v1, v2); 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | TEST(vaddw, s8) 13 | { 14 | //vaddw_type: 第一个vector元素宽度2倍于第二个vector元素;结果元素也是2倍宽度于第二个vector元素 15 | //WT v_out = vaddw_type(WT v1, T v2) 16 | int16x8_t v1 = vdupq_n_s16(300); 17 | int8x8_t v2 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 18 | int16x8_t actual = vaddw_s8(v1, v2); 19 | int16x8_t expected = { 301, 302, 303, 304, 305, 306, 307, 308 }; 20 | EXPECT_TRUE(almostEqual(expected, actual)); 21 | } -------------------------------------------------------------------------------- /tests/test_vand.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vand, u8) 4 | { 5 | uint8x8_t v1 = { 0, 9, 12, 11, 4, 13, 6, 17 }; 6 | uint8x8_t v2 = { 8, 11, 10, 3, 12, 5, 14, 15 }; 7 | 8 | uint8x8_t expected = { 0, 9, 8, 3, 4, 5, 6, 1 }; 9 | uint8x8_t actual = vand_u8(v1, v2); 10 | EXPECT_TRUE(almostEqual(expected, actual)); 11 | } -------------------------------------------------------------------------------- /tests/test_vext.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vext, u8) 4 | { 5 | { 6 | uint8x8_t src1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 7 | uint8x8_t src2 = { 9, 10, 11, 12, 13, 14, 15, 16 }; 8 | uint8x8_t expected = { 4, 5, 6, 7, 8, 9, 10, 11 }; 9 | uint8x8_t actual = vext_u8(src1, src2, 3); 10 | EXPECT_TRUE(almostEqual(expected, actual)); 11 | } 12 | 13 | { 14 | uint8_t s0_data[] = { 71, 61, 51, 41, 31, 21, 11, 1 }; 15 | uint8x8_t s0 = vld1_u8(s0_data); 16 | 17 | uint8x8_t m = vdup_n_u8(0); 18 | uint8x8_t s1 = vext_u8(m, s0, 7); // 0, 71, 61, 51, 41, 31, 21, 11, 19 | uint8x8_t s2 = vext_u8(m, s0, 6); // 0, 0, 71, 61, 51, 41, 31, 21, 20 | uint8x8_t s3 = vext_u8(m, s0, 5); 21 | uint8x8_t s4 = vext_u8(m, s0, 4); 22 | uint8x8_t s5 = vext_u8(m, s0, 3); 23 | uint8x8_t s6 = vext_u8(m, s0, 2); 24 | uint8x8_t s7 = vext_u8(m, s0, 1); 25 | 26 | uint8x8_t expected_s1 = { 0, 71, 61, 51, 41, 31, 21, 11 }; 27 | uint8x8_t expected_s2 = { 0, 0, 71, 61, 51, 41, 31, 21 }; 28 | uint8x8_t expected_s3 = { 0, 0, 0, 71, 61, 51, 41, 31 }; 29 | uint8x8_t expected_s4 = { 0, 0, 0, 0, 71, 61, 51, 41 }; 30 | uint8x8_t expected_s5 = { 0, 0, 0, 0, 0, 71, 61, 51 }; 31 | uint8x8_t expected_s6 = { 0, 0, 0, 0, 0, 0, 71, 61 }; 32 | uint8x8_t expected_s7 = { 0, 0, 0, 0, 0, 0, 0, 71 }; 33 | 34 | EXPECT_TRUE(almostEqual(expected_s1, s1)); 35 | EXPECT_TRUE(almostEqual(expected_s2, s2)); 36 | EXPECT_TRUE(almostEqual(expected_s3, s3)); 37 | EXPECT_TRUE(almostEqual(expected_s4, s4)); 38 | EXPECT_TRUE(almostEqual(expected_s5, s5)); 39 | EXPECT_TRUE(almostEqual(expected_s6, s6)); 40 | EXPECT_TRUE(almostEqual(expected_s7, s7)); 41 | } 42 | } -------------------------------------------------------------------------------- /tests/test_vget.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vget, low_s8) 4 | { 5 | int8x16_t v1 = { 6 | 1, 2, 3, 4, 5, 6, 7, 8, 7 | 11, 12, 13, 14, 15, 16, 17, 18 8 | }; 9 | int8x8_t actual = vget_low_s8(v1); 10 | int8x8_t expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; 11 | EXPECT_TRUE(almostEqual(expected, actual)); 12 | } -------------------------------------------------------------------------------- /tests/test_vhsub.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(sub, vhsub) 4 | { 5 | int8x8_t v1 = vdup_n_s8(120); 6 | int8x8_t v2 = { 1, 2, 3, 4, 120, 121, 122, 123 }; 7 | int8x8_t actual = vhsub_s8(v1, v2); 8 | int8x8_t expected = { 59, 59, 58, 58, 0, -1, -1, -2 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } -------------------------------------------------------------------------------- /tests/test_vld.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vld1q, f32) 4 | { 5 | float values[5] = { 1.0, 2.0, 3.0, 4.0, 5.0 }; 6 | float32x4_t actual = vld1q_f32(values); 7 | float32x4_t expected = { 1.0, 2.0, 3.0, 4.0 }; 8 | EXPECT_TRUE(almostEqual(expected, actual)); 9 | } 10 | 11 | TEST(vld1q_dup, f32) 12 | { 13 | float val = 3.0; 14 | float32x4_t expected = vld1q_dup_f32(&val); 15 | float32x4_t actual = { 3.0, 3.0, 3.0, 3.0 }; 16 | EXPECT_TRUE(almostEqual(expected, actual)); 17 | } -------------------------------------------------------------------------------- /tests/test_vmax_vmin.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vmaxq, f32) 4 | { 5 | float32x4_t v0 = { 5.0, 2.0, 3.0, 4.0 }; 6 | float32x4_t v1 = { 1.0, 6.0, 7.0, 8.0 }; 7 | float32x4_t actual = vmaxq_f32(v0, v1); 8 | float32x4_t expected = { 5.0, 6.0, 7.0, 8.0 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | TEST(vpmax, f32) 13 | { 14 | float32x4_t v0 = { 1.0, 2.0, 3.0, 4.0 }; 15 | float32x2_t maxOfHalfs = vpmax_f32(vget_low_f32(v0), vget_high_f32(v0)); 16 | float32x2_t maxOfMaxOfHalfs = vpmax_f32(maxOfHalfs, maxOfHalfs); 17 | float actual = vget_lane_f32(maxOfMaxOfHalfs, 0); 18 | float expected = 4.0; 19 | EXPECT_EQ(expected, actual); 20 | } 21 | 22 | TEST(vminq, f32) 23 | { 24 | float32x4_t v0 = { 5.0, 2.0, 3.0, 4.0 }; 25 | float32x4_t v1 = { 1.0, 6.0, 7.0, 8.0 }; 26 | float32x4_t actual = vminq_f32(v0, v1); 27 | float32x4_t expected = { 1.0, 2.0, 3.0, 4.0 }; 28 | EXPECT_TRUE(almostEqual(expected, actual)); 29 | } 30 | 31 | TEST(vpmin, f32) 32 | { 33 | float32x4_t v0 = { 1.0, 2.0, 3.0, 4.0 }; 34 | float32x2_t minOfHalfs = vpmin_f32(vget_low_f32(v0), vget_high_f32(v0)); 35 | float32x2_t minOfMinOfHalfs = vpmin_f32(minOfHalfs, minOfHalfs); 36 | float expected = vget_lane_f32(minOfMinOfHalfs, 0); 37 | float actual = 1.0; 38 | EXPECT_EQ(expected, actual); 39 | } -------------------------------------------------------------------------------- /tests/test_vmla.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vmlaq, f32) 4 | { 5 | float32x4_t v1 = { 1.0, 2.0, 3.0, 4.0 }; 6 | float32x4_t v2 = { 2.0, 2.0, 2.0, 2.0 }; 7 | float32x4_t v3 = { 3.0, 3.0, 3.0, 3.0 }; 8 | float32x4_t actual = vmlaq_f32(v3, v1, v2); // acc = v3 + v1 * v2 9 | float32x4_t expected = { 5.0, 7.0, 9.0, 11.0 }; 10 | EXPECT_TRUE(almostEqual(expected, actual)); 11 | } 12 | 13 | TEST(vmlaq_n, f32) 14 | { 15 | float32x4_t v1 = { 1.0, 2.0, 3.0, 4.0 }; 16 | float32x4_t v2 = { 1.0, 1.0, 1.0, 1.0 }; 17 | float32_t s = 3.0; 18 | float32x4_t actual = vmlaq_n_f32(v1, v2, s); 19 | float32x4_t expected = { 4.0, 5.0, 6.0, 7.0 }; 20 | EXPECT_TRUE(almostEqual(expected, actual)); 21 | } -------------------------------------------------------------------------------- /tests/test_vmov.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vmovq_n, f32) 4 | { 5 | float32x4_t actual = vmovq_n_f32(1.5); 6 | float32x4_t expected = { 1.5, 1.5, 1.5, 1.5 }; 7 | EXPECT_TRUE(almostEqual(expected, actual)); 8 | } -------------------------------------------------------------------------------- /tests/test_vmul.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vmulq, f32) 4 | { 5 | float32x4_t v1 = { 1.0, 2.0, 3.0, 4.0 }; 6 | float32x4_t v2 = { 1.0, 1.0, 1.0, 1.0 }; 7 | float32x4_t actual = vmulq_f32(v1, v2); 8 | float32x4_t expected = { 1.0, 2.0, 3.0, 4.0 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | TEST(vmulq_n, f32) 13 | { 14 | float32x4_t v = { 1.0, 2.0, 3.0, 4.0 }; 15 | float32_t s = 3.0; 16 | float32x4_t actual = vmulq_n_f32(v, s); 17 | float32x4_t expected = { 3.0, 6.0, 9.0, 12.0 }; 18 | EXPECT_TRUE(almostEqual(expected, actual)); 19 | } 20 | 21 | TEST(vmul, s8) 22 | { 23 | int8x8_t v1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 24 | int8x8_t v2 = { 2, 100, -10, -100, 2, 3, 4, 5 }; 25 | int8x8_t actual = vmul_s8(v1, v2); 26 | int8x8_t expected = { 2, -56, -30, 112, 10, 18, 28, 40 }; 27 | EXPECT_TRUE(almostEqual(expected, actual)); 28 | } 29 | 30 | TEST(mul, vmul_n_s16) 31 | { 32 | int16x4_t v1 = { 1, 2, 3, 4 }; 33 | int16_t s = 2; 34 | int16x4_t actual = vmul_n_s16(v1, s); 35 | int16x4_t expected = { 2, 4, 6, 8 }; 36 | EXPECT_TRUE(almostEqual(expected, actual)); 37 | } -------------------------------------------------------------------------------- /tests/test_vqsub.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(sub, vqsub) 4 | { 5 | // vqsub_type: ri = sat(ai + bi) 饱和指令,相减结果超出元素的最大值时,元素就取最大值。 6 | // 相减结果如果小于当前类型最小值, 则元素就取类型最小值。 7 | { 8 | uint8x8_t v1 = vdup_n_u8(250); 9 | uint8x8_t v2 = { 1, 2, 3, 4, 251, 252, 253, 254 }; 10 | uint8x8_t actual = vqsub_u8(v1, v2); 11 | uint8x8_t expected = { 249, 248, 247, 246, 0, 0, 0, 0 }; 12 | EXPECT_TRUE(almostEqual(expected, actual)); 13 | } 14 | 15 | { 16 | int8x8_t v1 = vdup_n_s8(120); 17 | int8x8_t v2 = { -11, 2, 3, 4, 121, 122, 123, 124 }; 18 | int8x8_t actual = vqsub_s8(v1, v2); 19 | int8x8_t expected = { 127, 118, 117, 116, -1, -2, -3, -4 }; 20 | EXPECT_TRUE(almostEqual(expected, actual)); 21 | } 22 | } -------------------------------------------------------------------------------- /tests/test_vrecpe.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | //- invert (needed for division): **vrecpeq_f32** or **vrecpeq_f64** 4 | TEST(vrecpeq, f32) 5 | { 6 | float32x4_t v = { 1.0, 2.0, 3.0, 4.0 }; 7 | float32x4_t actual = vrecpeq_f32(v); 8 | float32x4_t expected = { 0.998046875, 0.499023438, 0.333007813, 0.249511719 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | // invert (more accurately): use a Newton-Raphson iteration to refine the estimate 13 | // http://en.wikipedia.org/wiki/Division_algorithm#Newton.E2.80.93Raphson_division 14 | TEST(vrecpeq, f32_more_precise) 15 | { 16 | float32x4_t v = { 1.0, 2.0, 3.0, 4.0 }; 17 | float32x4_t reciprocal = vrecpeq_f32(v); 18 | float32x4_t actual = vmulq_f32(vrecpsq_f32(v, reciprocal), reciprocal); 19 | float32x4_t expected = { 0.999996185, 0.499998093, 0.333333015, 0.249999046 }; 20 | EXPECT_TRUE(almostEqual(expected, actual)); 21 | } -------------------------------------------------------------------------------- /tests/test_vrev.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vrev16, u8) 4 | { 5 | uint8x8_t src1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 6 | uint8x8_t actual = vrev16_u8(src1); 7 | uint8x8_t expected = { 2, 1, 4, 3, 6, 5, 8, 7 }; 8 | EXPECT_TRUE(almostEqual(expected, actual)); 9 | } 10 | 11 | TEST(vrev32, u8) 12 | { 13 | uint8x8_t src1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 14 | uint8x8_t actual = vrev32_u8(src1); 15 | uint8x8_t expected = { 4, 3, 2, 1, 8, 7, 6, 5 }; 16 | EXPECT_TRUE(almostEqual(expected, actual)); 17 | } 18 | 19 | TEST(vrev64, u8) 20 | { 21 | uint8x8_t src1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 22 | uint8x8_t actual = vrev64_u8(src1); 23 | uint8x8_t expected = { 8, 7, 6, 5, 4, 3, 2, 1 }; 24 | EXPECT_TRUE(almostEqual(expected, actual)); 25 | } -------------------------------------------------------------------------------- /tests/test_vrsubhn.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(sub, vrsubhn) 4 | { 5 | int16x8_t v1 = vdupq_n_s16(850); 6 | int16x8_t v2 = { 200, 400, 600, 800, 1000, 1200, 1400, 1600 }; 7 | int8x8_t actual = vrsubhn_s16(v1, v2); 8 | int8x8_t expected = { 3, 2, 1, 0, -1, -1, -2, -3 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | -------------------------------------------------------------------------------- /tests/test_vst.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vst1q, f32) 4 | { 5 | float32x4_t v = { 1.0, 2.0, 3.0, 4.0 }; 6 | std::vector actual = { 0, 0, 0, 0, 0 }; 7 | vst1q_f32(actual.data(), v); 8 | std::vector expected = { 1.0, 2.0, 3.0, 4.0, 0 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | //- store lane of array of vectors: **vst4q_lane_f16** or **vst4q_lane_f32** or **vst4q_lane_f64** (change to **vst1...** / **vst2...** / **vst3...** for other array lengths); 13 | TEST(vst4q_lane, f32) 14 | { 15 | float32x4_t v0 = { 1.0, 2.0, 3.0, 4.0 }; 16 | float32x4_t v1 = { 5.0, 6.0, 7.0, 8.0 }; 17 | float32x4_t v2 = { 9.0, 10.0, 11.0, 12.0 }; 18 | float32x4_t v3 = { 13.0, 14.0, 15.0, 16.0 }; 19 | float32x4x4_t u = { v0, v1, v2, v3 }; 20 | std::vector actual(4); 21 | vst4q_lane_f32(actual.data(), u, 0); 22 | std::vector expected = { 1.0, 5.0, 9.0, 13.0 }; 23 | EXPECT_TRUE(almostEqual(expected, actual)); 24 | } -------------------------------------------------------------------------------- /tests/test_vsub.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(sub, vsub) 4 | { 5 | // vsub_type: ri = ai - bi 6 | uint8x8_t v1 = { 11, 12, 13, 14, 15, 16, 17, 18 }; 7 | uint8x8_t v2 = vdup_n_u8(10); 8 | uint8x8_t actual = vsub_u8(v1, v2); 9 | uint8x8_t expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; 10 | EXPECT_TRUE(almostEqual(expected, actual)); 11 | } -------------------------------------------------------------------------------- /tests/test_vsubhn.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(sub, vsubhn) 4 | { 5 | // vsubhn_type: sub, half narrow 6 | // r = vsubhn_type(a, b), a和b是宽类型,r是(a-b)右移n/2位后的结果(高n/2位) 7 | int16x8_t v1 = { 100, 200, 300, 400, 500, 600, 700, 800 }; 8 | int16x8_t v2 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 9 | int8x8_t actual = vsubhn_s16(v1, v2); 10 | int8x8_t expected = { 0, 0, 1, 1, 1, 2, 2, 3 }; 11 | EXPECT_TRUE(almostEqual(expected, actual)); 12 | } -------------------------------------------------------------------------------- /tests/test_vsubl.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(sub, vsubl) 4 | { 5 | // vsubl_type: Signed Subtract Long. 6 | int8x8_t v1 = vdup_n_s8(127); 7 | int8x8_t v2 = { 0, 1, 2, 3, -1, -2, -3, -4 }; 8 | int16x8_t actual = vsubl_s8(v1, v2); 9 | int16x8_t expected = { 127, 126, 125, 124, 128, 129, 130, 131 }; 10 | EXPECT_TRUE(almostEqual(expected, actual)); 11 | } -------------------------------------------------------------------------------- /tests/test_vsubw.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(sub, vsubw) 4 | { 5 | // vsubw_type: r = vsubw_type(a, b), a和r是宽类型,b是窄类型 6 | int16x8_t v1 = { 101, 102, 103, 104, 105, 106, 107, 108 }; 7 | int8x8_t v2 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 8 | int16x8_t actual = vsubw_s8(v1, v2); 9 | int16x8_t expected = { 100, 100, 100, 100, 100, 100, 100, 100 }; 10 | EXPECT_TRUE(almostEqual(expected, actual)); 11 | } -------------------------------------------------------------------------------- /tests/test_vtbl.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vtbl1, u8) 4 | { 5 | uint8x8_t src1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; 6 | uint8x8_t src2 = { 0, 0, 1, 1, 2, 2, 7, 8 }; 7 | uint8x8_t actual = vtbl1_u8(src1, src2); 8 | uint8x8_t expected = { 1, 1, 2, 2, 3, 3, 8, 0 }; 9 | EXPECT_TRUE(almostEqual(expected, actual)); 10 | } 11 | 12 | TEST(vtbl2, u8) 13 | { 14 | uint8x8x2_t src; 15 | src.val[0] = uint8x8_t{ 1, 2, 3, 4, 5, 6, 7, 8 }; 16 | src.val[1] = uint8x8_t{ 9, 10, 11, 12, 13, 14, 15, 16 }; 17 | uint8x8_t src2 = { 0, 0, 1, 1, 2, 2, 8, 10 }; 18 | uint8x8_t actual = vtbl2_u8(src, src2); 19 | uint8x8_t expected = { 1, 1, 2, 2, 3, 3, 9, 11 }; 20 | EXPECT_TRUE(almostEqual(expected, actual)); 21 | } -------------------------------------------------------------------------------- /tests/test_vtrn.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vtrn, u8) 4 | { 5 | uint8x8x2_t src; 6 | src.val[0] = uint8x8_t{ 1, 2, 3, 4, 5, 6, 7, 8 }; 7 | src.val[1] = uint8x8_t{ 9, 10, 11, 12, 13, 14, 15, 16 }; 8 | uint8x8x2_t actual = vtrn_u8(src.val[0], src.val[1]); 9 | uint8x8x2_t expected; 10 | expected.val[0] = uint8x8_t{ 1, 9, 3, 11, 5, 13, 7, 15 }; 11 | expected.val[1] = uint8x8_t{ 2, 10, 4, 12, 6, 14, 8, 16 }; 12 | 13 | EXPECT_TRUE(almostEqual(expected, actual)); 14 | } -------------------------------------------------------------------------------- /tests/test_vuzp.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vuzp, u8) 4 | { 5 | uint8x8x2_t src; 6 | src.val[0] = uint8x8_t{ 1, 2, 3, 4, 5, 6, 7, 8 }; 7 | src.val[1] = uint8x8_t{ 9, 10, 11, 12, 13, 14, 15, 16 }; 8 | uint8x8x2_t actual = vuzp_u8(src.val[0], src.val[1]); 9 | uint8x8x2_t expected; 10 | expected.val[0] = uint8x8_t{ 1, 3, 5, 7, 9, 11, 13, 15 }; 11 | expected.val[1] = uint8x8_t{ 2, 4, 6, 8, 10, 12, 14, 16 }; 12 | 13 | EXPECT_TRUE(almostEqual(expected, actual)); 14 | } -------------------------------------------------------------------------------- /tests/test_vzip.cpp: -------------------------------------------------------------------------------- 1 | #include "test_util.hpp" 2 | 3 | TEST(vzip, u8) 4 | { 5 | uint8x8x2_t src; 6 | src.val[0] = uint8x8_t{ 1, 2, 3, 4, 5, 6, 7, 8 }; 7 | src.val[1] = uint8x8_t{ 9, 10, 11, 12, 13, 14, 15, 16 }; 8 | uint8x8x2_t actual = vzip_u8(src.val[0], src.val[1]); 9 | uint8x8x2_t expected; 10 | expected.val[0] = uint8x8_t{ 1, 9, 2, 10, 3, 11, 4, 12 }; 11 | expected.val[1] = uint8x8_t{ 5, 13, 6, 14, 7, 15, 8, 16 }; 12 | 13 | EXPECT_TRUE(almostEqual(expected, actual)); 14 | } -------------------------------------------------------------------------------- /tests/utest.h: -------------------------------------------------------------------------------- 1 | /* 2 | The latest version of this library is available on GitHub; 3 | https://github.com/sheredom/utest.h 4 | */ 5 | 6 | /* 7 | This is free and unencumbered software released into the public domain. 8 | 9 | Anyone is free to copy, modify, publish, use, compile, sell, or 10 | distribute this software, either in source code form or as a compiled 11 | binary, for any purpose, commercial or non-commercial, and by any 12 | means. 13 | 14 | In jurisdictions that recognize copyright laws, the author or authors 15 | of this software dedicate any and all copyright interest in the 16 | software to the public domain. We make this dedication for the benefit 17 | of the public at large and to the detriment of our heirs and 18 | successors. We intend this dedication to be an overt act of 19 | relinquishment in perpetuity of all present and future rights to this 20 | software under copyright law. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | For more information, please refer to 31 | */ 32 | 33 | #ifndef SHEREDOM_UTEST_H_INCLUDED 34 | #define SHEREDOM_UTEST_H_INCLUDED 35 | 36 | #ifdef _MSC_VER 37 | /* 38 | Disable warning about not inlining 'inline' functions. 39 | */ 40 | #pragma warning(disable : 4710) 41 | 42 | /* 43 | Disable warning about inlining functions that are not marked 'inline'. 44 | */ 45 | #pragma warning(disable : 4711) 46 | 47 | /* 48 | Disable warning for alignment padding added 49 | */ 50 | #pragma warning(disable : 4820) 51 | 52 | #if _MSC_VER > 1900 53 | /* 54 | Disable warning about preprocessor macros not being defined in MSVC headers. 55 | */ 56 | #pragma warning(disable : 4668) 57 | 58 | /* 59 | Disable warning about no function prototype given in MSVC headers. 60 | */ 61 | #pragma warning(disable : 4255) 62 | 63 | /* 64 | Disable warning about pointer or reference to potentially throwing function. 65 | */ 66 | #pragma warning(disable : 5039) 67 | #endif 68 | 69 | #pragma warning(push, 1) 70 | #endif 71 | 72 | #if defined(_MSC_VER) && (_MSC_VER < 1920) 73 | typedef __int64 utest_int64_t; 74 | typedef unsigned __int64 utest_uint64_t; 75 | typedef unsigned __int32 utest_uint32_t; 76 | #else 77 | #include 78 | typedef int64_t utest_int64_t; 79 | typedef uint64_t utest_uint64_t; 80 | typedef uint32_t utest_uint32_t; 81 | #endif 82 | 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | 89 | #if defined(__cplusplus) 90 | #include 91 | #endif 92 | 93 | #if defined(_MSC_VER) 94 | #pragma warning(pop) 95 | #endif 96 | 97 | #if defined(__cplusplus) 98 | #define UTEST_C_FUNC extern "C" 99 | #else 100 | #define UTEST_C_FUNC 101 | #endif 102 | 103 | #define UTEST_TEST_PASSED (0) 104 | #define UTEST_TEST_FAILURE (1) 105 | #define UTEST_TEST_SKIPPED (2) 106 | 107 | #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) 108 | 109 | #if defined(__MINGW64__) || defined(__MINGW32__) 110 | #pragma GCC diagnostic push 111 | #pragma GCC diagnostic ignored "-Wpragmas" 112 | #pragma GCC diagnostic ignored "-Wunknown-pragmas" 113 | #endif 114 | 115 | // define UTEST_USE_OLD_QPC before #include "utest.h" to use old 116 | // QueryPerformanceCounter 117 | #ifndef UTEST_USE_OLD_QPC 118 | #pragma warning(push, 0) 119 | #include 120 | #pragma warning(pop) 121 | 122 | typedef LARGE_INTEGER utest_large_integer; 123 | #else 124 | // use old QueryPerformanceCounter definitions (not sure is this needed in some 125 | // edge cases or not) on Win7 with VS2015 these extern declaration cause "second 126 | // C linkage of overloaded function not allowed" error 127 | typedef union { 128 | struct { 129 | unsigned long LowPart; 130 | long HighPart; 131 | } DUMMYSTRUCTNAME; 132 | struct { 133 | unsigned long LowPart; 134 | long HighPart; 135 | } u; 136 | utest_int64_t QuadPart; 137 | } utest_large_integer; 138 | 139 | UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceCounter( 140 | utest_large_integer *); 141 | UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( 142 | utest_large_integer *); 143 | 144 | #if defined(__MINGW64__) || defined(__MINGW32__) 145 | #pragma GCC diagnostic pop 146 | #endif 147 | #endif 148 | 149 | #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ 150 | defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ 151 | defined(__HAIKU__) 152 | /* 153 | slightly obscure include here - we need to include glibc's features.h, but 154 | we don't want to just include a header that might not be defined for other 155 | c libraries like musl. Instead we include limits.h, which we know on all 156 | glibc distributions includes features.h 157 | */ 158 | #include 159 | 160 | #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) 161 | #include 162 | 163 | #if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__))) 164 | /* glibc is version 2.17 or above, so we can just use clock_gettime */ 165 | #define UTEST_USE_CLOCKGETTIME 166 | #else 167 | #include 168 | #include 169 | #endif 170 | #else // Other libc implementations 171 | #include 172 | #define UTEST_USE_CLOCKGETTIME 173 | #endif 174 | 175 | #elif defined(__APPLE__) 176 | #include 177 | #endif 178 | 179 | #if defined(_MSC_VER) && (_MSC_VER < 1920) 180 | #define UTEST_PRId64 "I64d" 181 | #define UTEST_PRIu64 "I64u" 182 | #else 183 | #include 184 | 185 | #define UTEST_PRId64 PRId64 186 | #define UTEST_PRIu64 PRIu64 187 | #endif 188 | 189 | #if defined(__cplusplus) 190 | #define UTEST_INLINE inline 191 | 192 | #if defined(__clang__) 193 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 194 | _Pragma("clang diagnostic push") \ 195 | _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") 196 | 197 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") 198 | #else 199 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS 200 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS 201 | #endif 202 | 203 | #define UTEST_INITIALIZER(f) \ 204 | struct f##_cpp_struct { \ 205 | f##_cpp_struct(); \ 206 | }; \ 207 | UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS static f##_cpp_struct \ 208 | f##_cpp_global UTEST_INITIALIZER_END_DISABLE_WARNINGS; \ 209 | f##_cpp_struct::f##_cpp_struct() 210 | #elif defined(_MSC_VER) 211 | #define UTEST_INLINE __forceinline 212 | 213 | #if defined(_WIN64) 214 | #define UTEST_SYMBOL_PREFIX 215 | #else 216 | #define UTEST_SYMBOL_PREFIX "_" 217 | #endif 218 | 219 | #if defined(__clang__) 220 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 221 | _Pragma("clang diagnostic push") \ 222 | _Pragma("clang diagnostic ignored \"-Wmissing-variable-declarations\"") 223 | 224 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") 225 | #else 226 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS 227 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS 228 | #endif 229 | 230 | #pragma section(".CRT$XCU", read) 231 | #define UTEST_INITIALIZER(f) \ 232 | static void __cdecl f(void); \ 233 | UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 234 | __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ 235 | UTEST_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ 236 | f##_)(void) = f; \ 237 | UTEST_INITIALIZER_END_DISABLE_WARNINGS \ 238 | static void __cdecl f(void) 239 | #else 240 | #if defined(__linux__) 241 | #if defined(__clang__) 242 | #if __has_warning("-Wreserved-id-macro") 243 | #pragma clang diagnostic push 244 | #pragma clang diagnostic ignored "-Wreserved-id-macro" 245 | #endif 246 | #endif 247 | 248 | #define __STDC_FORMAT_MACROS 1 249 | 250 | #if defined(__clang__) 251 | #if __has_warning("-Wreserved-id-macro") 252 | #pragma clang diagnostic pop 253 | #endif 254 | #endif 255 | #endif 256 | 257 | #define UTEST_INLINE inline 258 | 259 | #define UTEST_INITIALIZER(f) \ 260 | static void f(void) __attribute__((constructor)); \ 261 | static void f(void) 262 | #endif 263 | 264 | #if defined(__cplusplus) 265 | #define UTEST_CAST(type, x) static_cast(x) 266 | #define UTEST_PTR_CAST(type, x) reinterpret_cast(x) 267 | #define UTEST_EXTERN extern "C" 268 | #define UTEST_NULL NULL 269 | #else 270 | #define UTEST_CAST(type, x) ((type)(x)) 271 | #define UTEST_PTR_CAST(type, x) ((type)(x)) 272 | #define UTEST_EXTERN extern 273 | #define UTEST_NULL 0 274 | #endif 275 | 276 | #ifdef _MSC_VER 277 | /* 278 | io.h contains definitions for some structures with natural padding. This is 279 | uninteresting, but for some reason MSVC's behaviour is to warn about 280 | including this system header. That *is* interesting 281 | */ 282 | #pragma warning(disable : 4820) 283 | #pragma warning(push, 1) 284 | #include 285 | #pragma warning(pop) 286 | #define UTEST_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) 287 | #else 288 | #if defined(__EMSCRIPTEN__) 289 | #include 290 | #define UTEST_COLOUR_OUTPUT() false 291 | #else 292 | #include 293 | #define UTEST_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) 294 | #endif 295 | #endif 296 | 297 | static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) { 298 | void *const new_pointer = realloc(pointer, new_size); 299 | 300 | if (UTEST_NULL == new_pointer) { 301 | free(new_pointer); 302 | } 303 | 304 | return new_pointer; 305 | } 306 | 307 | static UTEST_INLINE utest_int64_t utest_ns(void) { 308 | #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) 309 | utest_large_integer counter; 310 | utest_large_integer frequency; 311 | QueryPerformanceCounter(&counter); 312 | QueryPerformanceFrequency(&frequency); 313 | return UTEST_CAST(utest_int64_t, 314 | (counter.QuadPart * 1000000000) / frequency.QuadPart); 315 | #elif defined(__linux__) && defined(__STRICT_ANSI__) 316 | return UTEST_CAST(utest_int64_t, clock()) * 1000000000 / CLOCKS_PER_SEC; 317 | #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ 318 | defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ 319 | defined(__HAIKU__) 320 | struct timespec ts; 321 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ 322 | !defined(__HAIKU__) 323 | timespec_get(&ts, TIME_UTC); 324 | #else 325 | const clockid_t cid = CLOCK_REALTIME; 326 | #if defined(UTEST_USE_CLOCKGETTIME) 327 | clock_gettime(cid, &ts); 328 | #else 329 | syscall(SYS_clock_gettime, cid, &ts); 330 | #endif 331 | #endif 332 | return UTEST_CAST(utest_int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; 333 | #elif __APPLE__ 334 | return UTEST_CAST(utest_int64_t, mach_absolute_time()); 335 | #elif __EMSCRIPTEN__ 336 | return emscripten_performance_now() * 1000000.0; 337 | #else 338 | #error Unsupported platform! 339 | #endif 340 | } 341 | 342 | typedef void (*utest_testcase_t)(int *, size_t); 343 | 344 | struct utest_test_state_s { 345 | utest_testcase_t func; 346 | size_t index; 347 | char *name; 348 | }; 349 | 350 | struct utest_state_s { 351 | struct utest_test_state_s *tests; 352 | size_t tests_length; 353 | FILE *output; 354 | }; 355 | 356 | /* extern to the global state utest needs to execute */ 357 | UTEST_EXTERN struct utest_state_s utest_state; 358 | 359 | #if defined(_MSC_VER) 360 | #define UTEST_WEAK __forceinline 361 | #else 362 | #define UTEST_WEAK __attribute__((weak)) 363 | #endif 364 | 365 | #if defined(_MSC_VER) 366 | #define UTEST_UNUSED 367 | #else 368 | #define UTEST_UNUSED __attribute__((unused)) 369 | #endif 370 | 371 | #ifdef __clang__ 372 | #pragma clang diagnostic push 373 | #pragma clang diagnostic ignored "-Wvariadic-macros" 374 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 375 | #endif 376 | #define UTEST_PRINTF(...) \ 377 | if (utest_state.output) { \ 378 | fprintf(utest_state.output, __VA_ARGS__); \ 379 | } \ 380 | printf(__VA_ARGS__) 381 | #ifdef __clang__ 382 | #pragma clang diagnostic pop 383 | #endif 384 | 385 | #ifdef __clang__ 386 | #pragma clang diagnostic push 387 | #pragma clang diagnostic ignored "-Wvariadic-macros" 388 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 389 | #endif 390 | 391 | #ifdef _MSC_VER 392 | #define UTEST_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) 393 | #else 394 | #define UTEST_SNPRINTF(...) snprintf(__VA_ARGS__) 395 | #endif 396 | 397 | #ifdef __clang__ 398 | #pragma clang diagnostic pop 399 | #endif 400 | 401 | #if defined(__cplusplus) 402 | /* if we are using c++ we can use overloaded methods (its in the language) */ 403 | #define UTEST_OVERLOADABLE 404 | #elif defined(__clang__) 405 | /* otherwise, if we are using clang with c - use the overloadable attribute */ 406 | #define UTEST_OVERLOADABLE __attribute__((overloadable)) 407 | #endif 408 | 409 | #if defined(UTEST_OVERLOADABLE) 410 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); 411 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { 412 | UTEST_PRINTF("%f", UTEST_CAST(double, f)); 413 | } 414 | 415 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); 416 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { 417 | UTEST_PRINTF("%f", d); 418 | } 419 | 420 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); 421 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { 422 | UTEST_PRINTF("%Lf", d); 423 | } 424 | 425 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); 426 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { 427 | UTEST_PRINTF("%d", i); 428 | } 429 | 430 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i); 431 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) { 432 | UTEST_PRINTF("%u", i); 433 | } 434 | 435 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i); 436 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) { 437 | UTEST_PRINTF("%ld", i); 438 | } 439 | 440 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i); 441 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) { 442 | UTEST_PRINTF("%lu", i); 443 | } 444 | 445 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p); 446 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p) { 447 | UTEST_PRINTF("%p", p); 448 | } 449 | 450 | /* 451 | long long is a c++11 extension 452 | */ 453 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || \ 454 | defined(__cplusplus) && (__cplusplus >= 201103L) 455 | 456 | #ifdef __clang__ 457 | #pragma clang diagnostic push 458 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 459 | #endif 460 | 461 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); 462 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { 463 | UTEST_PRINTF("%lld", i); 464 | } 465 | 466 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i); 467 | UTEST_WEAK UTEST_OVERLOADABLE void 468 | utest_type_printer(long long unsigned int i) { 469 | UTEST_PRINTF("%llu", i); 470 | } 471 | 472 | #ifdef __clang__ 473 | #pragma clang diagnostic pop 474 | #endif 475 | 476 | #endif 477 | #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) 478 | #define utest_type_printer(val) \ 479 | UTEST_PRINTF(_Generic((val), signed char \ 480 | : "%d", unsigned char \ 481 | : "%u", short \ 482 | : "%d", unsigned short \ 483 | : "%u", int \ 484 | : "%d", long \ 485 | : "%ld", long long \ 486 | : "%lld", unsigned \ 487 | : "%u", unsigned long \ 488 | : "%lu", unsigned long long \ 489 | : "%llu", float \ 490 | : "%f", double \ 491 | : "%f", long double \ 492 | : "%Lf", default \ 493 | : _Generic((val - val), ptrdiff_t \ 494 | : "%p", default \ 495 | : "undef")), \ 496 | (val)) 497 | #else 498 | /* 499 | we don't have the ability to print the values we got, so we create a macro 500 | to tell our users we can't do anything fancy 501 | */ 502 | #define utest_type_printer(...) UTEST_PRINTF("undef") 503 | #endif 504 | 505 | #ifdef _MSC_VER 506 | #define UTEST_SURPRESS_WARNING_BEGIN \ 507 | __pragma(warning(push)) __pragma(warning(disable : 4127)) \ 508 | __pragma(warning(disable : 4571)) __pragma(warning(disable : 4130)) 509 | #define UTEST_SURPRESS_WARNING_END __pragma(warning(pop)) 510 | #else 511 | #define UTEST_SURPRESS_WARNING_BEGIN 512 | #define UTEST_SURPRESS_WARNING_END 513 | #endif 514 | 515 | #if defined(__cplusplus) && (__cplusplus >= 201103L) 516 | #define UTEST_AUTO(x) auto 517 | #elif !defined(__cplusplus) 518 | 519 | #if defined(__clang__) 520 | /* clang-format off */ 521 | /* had to disable clang-format here because it malforms the pragmas */ 522 | #define UTEST_AUTO(x) \ 523 | _Pragma("clang diagnostic push") \ 524 | _Pragma("clang diagnostic ignored \"-Wgnu-auto-type\"") __auto_type \ 525 | _Pragma("clang diagnostic pop") 526 | /* clang-format on */ 527 | #else 528 | #define UTEST_AUTO(x) __typeof__(x + 0) 529 | #endif 530 | 531 | #else 532 | #define UTEST_AUTO(x) typeof(x + 0) 533 | #endif 534 | 535 | #if defined(__clang__) 536 | #define UTEST_STRNCMP(x, y, size) \ 537 | _Pragma("clang diagnostic push") \ 538 | _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ 539 | strncmp(x, y, size) _Pragma("clang diagnostic pop") 540 | #else 541 | #define UTEST_STRNCMP(x, y, size) strncmp(x, y, size) 542 | #endif 543 | 544 | #define UTEST_SKIP(msg) \ 545 | do { \ 546 | UTEST_PRINTF(" Skipped : '%s'\n", (msg)); \ 547 | *utest_result = UTEST_TEST_SKIPPED; \ 548 | return; \ 549 | } while (0) 550 | 551 | #if defined(__clang__) 552 | #define UTEST_EXPECT(x, y, cond) \ 553 | UTEST_SURPRESS_WARNING_BEGIN do { \ 554 | _Pragma("clang diagnostic push") \ 555 | _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ 556 | _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ 557 | _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ 558 | UTEST_AUTO(x) xEval = (x); \ 559 | UTEST_AUTO(y) yEval = (y); \ 560 | if (!((xEval)cond(yEval))) { \ 561 | _Pragma("clang diagnostic pop") \ 562 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 563 | UTEST_PRINTF(" Expected : ("); \ 564 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 565 | UTEST_PRINTF(")\n"); \ 566 | UTEST_PRINTF(" Actual : "); \ 567 | utest_type_printer(xEval); \ 568 | UTEST_PRINTF(" vs "); \ 569 | utest_type_printer(yEval); \ 570 | UTEST_PRINTF("\n"); \ 571 | *utest_result = UTEST_TEST_FAILURE; \ 572 | } \ 573 | } \ 574 | while (0) \ 575 | UTEST_SURPRESS_WARNING_END 576 | #elif defined(__GNUC__) 577 | #define UTEST_EXPECT(x, y, cond) \ 578 | UTEST_SURPRESS_WARNING_BEGIN do { \ 579 | UTEST_AUTO(x) xEval = (x); \ 580 | UTEST_AUTO(y) yEval = (y); \ 581 | if (!((xEval)cond(yEval))) { \ 582 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 583 | UTEST_PRINTF(" Expected : ("); \ 584 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 585 | UTEST_PRINTF(")\n"); \ 586 | UTEST_PRINTF(" Actual : "); \ 587 | utest_type_printer(xEval); \ 588 | UTEST_PRINTF(" vs "); \ 589 | utest_type_printer(yEval); \ 590 | UTEST_PRINTF("\n"); \ 591 | *utest_result = UTEST_TEST_FAILURE; \ 592 | } \ 593 | } \ 594 | while (0) \ 595 | UTEST_SURPRESS_WARNING_END 596 | #else 597 | #define UTEST_EXPECT(x, y, cond) \ 598 | UTEST_SURPRESS_WARNING_BEGIN do { \ 599 | if (!((x)cond(y))) { \ 600 | UTEST_PRINTF("%s:%u: Failure (Expected " #cond " Actual)\n", __FILE__, \ 601 | __LINE__); \ 602 | *utest_result = UTEST_TEST_FAILURE; \ 603 | } \ 604 | } \ 605 | while (0) \ 606 | UTEST_SURPRESS_WARNING_END 607 | #endif 608 | 609 | #define EXPECT_TRUE(x) \ 610 | UTEST_SURPRESS_WARNING_BEGIN do { \ 611 | if (!(x)) { \ 612 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 613 | UTEST_PRINTF(" Expected : true\n"); \ 614 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 615 | *utest_result = UTEST_TEST_FAILURE; \ 616 | } \ 617 | } \ 618 | while (0) \ 619 | UTEST_SURPRESS_WARNING_END 620 | 621 | #define EXPECT_FALSE(x) \ 622 | UTEST_SURPRESS_WARNING_BEGIN do { \ 623 | if (x) { \ 624 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 625 | UTEST_PRINTF(" Expected : false\n"); \ 626 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 627 | *utest_result = UTEST_TEST_FAILURE; \ 628 | } \ 629 | } \ 630 | while (0) \ 631 | UTEST_SURPRESS_WARNING_END 632 | 633 | #define EXPECT_EQ(x, y) UTEST_EXPECT(x, y, ==) 634 | #define EXPECT_NE(x, y) UTEST_EXPECT(x, y, !=) 635 | #define EXPECT_LT(x, y) UTEST_EXPECT(x, y, <) 636 | #define EXPECT_LE(x, y) UTEST_EXPECT(x, y, <=) 637 | #define EXPECT_GT(x, y) UTEST_EXPECT(x, y, >) 638 | #define EXPECT_GE(x, y) UTEST_EXPECT(x, y, >=) 639 | 640 | #define EXPECT_STREQ(x, y) \ 641 | UTEST_SURPRESS_WARNING_BEGIN do { \ 642 | if (UTEST_NULL == x || UTEST_NULL == y || 0 != strcmp(x, y)) { \ 643 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 644 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 645 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 646 | *utest_result = UTEST_TEST_FAILURE; \ 647 | } \ 648 | } \ 649 | while (0) \ 650 | UTEST_SURPRESS_WARNING_END 651 | 652 | #define EXPECT_STRNE(x, y) \ 653 | UTEST_SURPRESS_WARNING_BEGIN do { \ 654 | if (UTEST_NULL == x || UTEST_NULL == y || 0 == strcmp(x, y)) { \ 655 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 656 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 657 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 658 | *utest_result = UTEST_TEST_FAILURE; \ 659 | } \ 660 | } \ 661 | while (0) \ 662 | UTEST_SURPRESS_WARNING_END 663 | 664 | #define EXPECT_STRNEQ(x, y, n) \ 665 | UTEST_SURPRESS_WARNING_BEGIN do { \ 666 | if (UTEST_NULL == x || UTEST_NULL == y || 0 != UTEST_STRNCMP(x, y, n)) { \ 667 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 668 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 669 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 670 | *utest_result = UTEST_TEST_FAILURE; \ 671 | } \ 672 | } \ 673 | while (0) \ 674 | UTEST_SURPRESS_WARNING_END 675 | 676 | #define EXPECT_STRNNE(x, y, n) \ 677 | UTEST_SURPRESS_WARNING_BEGIN do { \ 678 | if (UTEST_NULL == x || UTEST_NULL == y || 0 == UTEST_STRNCMP(x, y, n)) { \ 679 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 680 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 681 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 682 | *utest_result = UTEST_TEST_FAILURE; \ 683 | } \ 684 | } \ 685 | while (0) \ 686 | UTEST_SURPRESS_WARNING_END 687 | 688 | #define EXPECT_NEAR(x, y, epsilon) \ 689 | UTEST_SURPRESS_WARNING_BEGIN do { \ 690 | const double diff = \ 691 | utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)); \ 692 | if (diff > UTEST_CAST(double, epsilon) || utest_isnan(diff)) { \ 693 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 694 | UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ 695 | UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ 696 | *utest_result = UTEST_TEST_FAILURE; \ 697 | } \ 698 | } \ 699 | while (0) \ 700 | UTEST_SURPRESS_WARNING_END 701 | 702 | #if defined(__cplusplus) 703 | #define EXPECT_EXCEPTION(x, exception_type) \ 704 | UTEST_SURPRESS_WARNING_BEGIN do { \ 705 | int exception_caught = 0; \ 706 | try { \ 707 | x; \ 708 | } catch (const exception_type &) { \ 709 | exception_caught = 1; \ 710 | } catch (...) { \ 711 | exception_caught = 2; \ 712 | } \ 713 | if (exception_caught != 1) { \ 714 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 715 | UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ 716 | UTEST_PRINTF(" Actual : %s\n", (exception_caught == 2) \ 717 | ? "Unexpected exception" \ 718 | : "No exception"); \ 719 | *utest_result = UTEST_TEST_FAILURE; \ 720 | } \ 721 | } \ 722 | while (0) \ 723 | UTEST_SURPRESS_WARNING_END 724 | #endif 725 | 726 | #if defined(__clang__) 727 | #define UTEST_ASSERT(x, y, cond) \ 728 | UTEST_SURPRESS_WARNING_BEGIN do { \ 729 | _Pragma("clang diagnostic push") \ 730 | _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ 731 | _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ 732 | _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ 733 | UTEST_AUTO(x) xEval = (x); \ 734 | UTEST_AUTO(y) yEval = (y); \ 735 | if (!((xEval)cond(yEval))) { \ 736 | _Pragma("clang diagnostic pop") \ 737 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 738 | UTEST_PRINTF(" Expected : ("); \ 739 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 740 | UTEST_PRINTF(")\n"); \ 741 | UTEST_PRINTF(" Actual : "); \ 742 | utest_type_printer(xEval); \ 743 | UTEST_PRINTF(" vs "); \ 744 | utest_type_printer(yEval); \ 745 | UTEST_PRINTF("\n"); \ 746 | *utest_result = UTEST_TEST_FAILURE; \ 747 | return; \ 748 | } \ 749 | } \ 750 | while (0) \ 751 | UTEST_SURPRESS_WARNING_END 752 | #elif defined(__GNUC__) 753 | #define UTEST_ASSERT(x, y, cond) \ 754 | UTEST_SURPRESS_WARNING_BEGIN do { \ 755 | UTEST_AUTO(x) xEval = (x); \ 756 | UTEST_AUTO(y) yEval = (y); \ 757 | if (!((xEval)cond(yEval))) { \ 758 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 759 | UTEST_PRINTF(" Expected : ("); \ 760 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 761 | UTEST_PRINTF(")\n"); \ 762 | UTEST_PRINTF(" Actual : "); \ 763 | utest_type_printer(xEval); \ 764 | UTEST_PRINTF(" vs "); \ 765 | utest_type_printer(yEval); \ 766 | UTEST_PRINTF("\n"); \ 767 | *utest_result = UTEST_TEST_FAILURE; \ 768 | return; \ 769 | } \ 770 | } \ 771 | while (0) \ 772 | UTEST_SURPRESS_WARNING_END 773 | #else 774 | #define UTEST_ASSERT(x, y, cond) \ 775 | UTEST_SURPRESS_WARNING_BEGIN do { \ 776 | if (!((x)cond(y))) { \ 777 | UTEST_PRINTF("%s:%u: Failure (Expected " #cond " Actual)\n", __FILE__, \ 778 | __LINE__); \ 779 | *utest_result = UTEST_TEST_FAILURE; \ 780 | return; \ 781 | } \ 782 | } \ 783 | while (0) \ 784 | UTEST_SURPRESS_WARNING_END 785 | #endif 786 | 787 | #define ASSERT_TRUE(x) \ 788 | UTEST_SURPRESS_WARNING_BEGIN do { \ 789 | if (!(x)) { \ 790 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 791 | UTEST_PRINTF(" Expected : true\n"); \ 792 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 793 | *utest_result = UTEST_TEST_FAILURE; \ 794 | return; \ 795 | } \ 796 | } \ 797 | while (0) \ 798 | UTEST_SURPRESS_WARNING_END 799 | 800 | #define ASSERT_FALSE(x) \ 801 | UTEST_SURPRESS_WARNING_BEGIN do { \ 802 | if (x) { \ 803 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 804 | UTEST_PRINTF(" Expected : false\n"); \ 805 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 806 | *utest_result = UTEST_TEST_FAILURE; \ 807 | return; \ 808 | } \ 809 | } \ 810 | while (0) \ 811 | UTEST_SURPRESS_WARNING_END 812 | 813 | #define ASSERT_EQ(x, y) UTEST_ASSERT(x, y, ==) 814 | #define ASSERT_NE(x, y) UTEST_ASSERT(x, y, !=) 815 | #define ASSERT_LT(x, y) UTEST_ASSERT(x, y, <) 816 | #define ASSERT_LE(x, y) UTEST_ASSERT(x, y, <=) 817 | #define ASSERT_GT(x, y) UTEST_ASSERT(x, y, >) 818 | #define ASSERT_GE(x, y) UTEST_ASSERT(x, y, >=) 819 | 820 | #define ASSERT_STREQ(x, y) \ 821 | UTEST_SURPRESS_WARNING_BEGIN do { \ 822 | if (UTEST_NULL == x || UTEST_NULL == y || 0 != strcmp(x, y)) { \ 823 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 824 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 825 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 826 | *utest_result = UTEST_TEST_FAILURE; \ 827 | return; \ 828 | } \ 829 | } \ 830 | while (0) \ 831 | UTEST_SURPRESS_WARNING_END 832 | 833 | #define ASSERT_STRNE(x, y) \ 834 | UTEST_SURPRESS_WARNING_BEGIN do { \ 835 | if (UTEST_NULL == x || UTEST_NULL == y || 0 == strcmp(x, y)) { \ 836 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 837 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 838 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 839 | *utest_result = UTEST_TEST_FAILURE; \ 840 | return; \ 841 | } \ 842 | } \ 843 | while (0) \ 844 | UTEST_SURPRESS_WARNING_END 845 | 846 | #define ASSERT_STRNEQ(x, y, n) \ 847 | UTEST_SURPRESS_WARNING_BEGIN do { \ 848 | if (UTEST_NULL == x || UTEST_NULL == y || 0 != UTEST_STRNCMP(x, y, n)) { \ 849 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 850 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 851 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 852 | *utest_result = UTEST_TEST_FAILURE; \ 853 | return; \ 854 | } \ 855 | } \ 856 | while (0) \ 857 | UTEST_SURPRESS_WARNING_END 858 | 859 | #define ASSERT_STRNNE(x, y, n) \ 860 | UTEST_SURPRESS_WARNING_BEGIN do { \ 861 | if (UTEST_NULL == x || UTEST_NULL == y || 0 == UTEST_STRNCMP(x, y, n)) { \ 862 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 863 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 864 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 865 | *utest_result = UTEST_TEST_FAILURE; \ 866 | return; \ 867 | } \ 868 | } \ 869 | while (0) \ 870 | UTEST_SURPRESS_WARNING_END 871 | 872 | #define ASSERT_NEAR(x, y, epsilon) \ 873 | UTEST_SURPRESS_WARNING_BEGIN do { \ 874 | const double diff = \ 875 | utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)); \ 876 | if (diff > UTEST_CAST(double, epsilon) || utest_isnan(diff)) { \ 877 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 878 | UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ 879 | UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ 880 | *utest_result = UTEST_TEST_FAILURE; \ 881 | return; \ 882 | } \ 883 | } \ 884 | while (0) \ 885 | UTEST_SURPRESS_WARNING_END 886 | 887 | #if defined(__cplusplus) 888 | #define ASSERT_EXCEPTION(x, exception_type) \ 889 | UTEST_SURPRESS_WARNING_BEGIN do { \ 890 | int exception_caught = 0; \ 891 | try { \ 892 | x; \ 893 | } catch (const exception_type &) { \ 894 | exception_caught = 1; \ 895 | } catch (...) { \ 896 | exception_caught = 2; \ 897 | } \ 898 | if (exception_caught != 1) { \ 899 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 900 | UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ 901 | UTEST_PRINTF(" Actual : %s\n", (exception_caught == 2) \ 902 | ? "Unexpected exception" \ 903 | : "No exception"); \ 904 | *utest_result = UTEST_TEST_FAILURE; \ 905 | return; \ 906 | } \ 907 | } \ 908 | while (0) \ 909 | UTEST_SURPRESS_WARNING_END 910 | #endif 911 | 912 | #define UTEST(SET, NAME) \ 913 | UTEST_EXTERN struct utest_state_s utest_state; \ 914 | static void utest_run_##SET##_##NAME(int *utest_result); \ 915 | static void utest_##SET##_##NAME(int *utest_result, size_t utest_index) { \ 916 | (void)utest_index; \ 917 | utest_run_##SET##_##NAME(utest_result); \ 918 | } \ 919 | UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ 920 | const size_t index = utest_state.tests_length++; \ 921 | const char *name_part = #SET "." #NAME; \ 922 | const size_t name_size = strlen(name_part) + 1; \ 923 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 924 | utest_state.tests = UTEST_PTR_CAST( \ 925 | struct utest_test_state_s *, \ 926 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 927 | sizeof(struct utest_test_state_s) * \ 928 | utest_state.tests_length)); \ 929 | if (utest_state.tests) { \ 930 | utest_state.tests[index].func = &utest_##SET##_##NAME; \ 931 | utest_state.tests[index].name = name; \ 932 | utest_state.tests[index].index = 0; \ 933 | } \ 934 | UTEST_SNPRINTF(name, name_size, "%s", name_part); \ 935 | } \ 936 | void utest_run_##SET##_##NAME(int *utest_result) 937 | 938 | #define UTEST_F_SETUP(FIXTURE) \ 939 | static void utest_f_setup_##FIXTURE(int *utest_result, \ 940 | struct FIXTURE *utest_fixture) 941 | 942 | #define UTEST_F_TEARDOWN(FIXTURE) \ 943 | static void utest_f_teardown_##FIXTURE(int *utest_result, \ 944 | struct FIXTURE *utest_fixture) 945 | 946 | #if defined(__GNUC__) && __GNUC__ >= 8 && defined(__cplusplus) 947 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN \ 948 | _Pragma("GCC diagnostic push") \ 949 | _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") 950 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_END _Pragma("GCC diagnostic pop") 951 | #else 952 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN 953 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_END 954 | #endif 955 | 956 | #define UTEST_F(FIXTURE, NAME) \ 957 | UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN \ 958 | UTEST_EXTERN struct utest_state_s utest_state; \ 959 | static void utest_f_setup_##FIXTURE(int *, struct FIXTURE *); \ 960 | static void utest_f_teardown_##FIXTURE(int *, struct FIXTURE *); \ 961 | static void utest_run_##FIXTURE##_##NAME(int *, struct FIXTURE *); \ 962 | static void utest_f_##FIXTURE##_##NAME(int *utest_result, \ 963 | size_t utest_index) { \ 964 | struct FIXTURE fixture; \ 965 | (void)utest_index; \ 966 | memset(&fixture, 0, sizeof(fixture)); \ 967 | utest_f_setup_##FIXTURE(utest_result, &fixture); \ 968 | if (UTEST_TEST_PASSED != *utest_result) { \ 969 | return; \ 970 | } \ 971 | utest_run_##FIXTURE##_##NAME(utest_result, &fixture); \ 972 | utest_f_teardown_##FIXTURE(utest_result, &fixture); \ 973 | } \ 974 | UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ 975 | const size_t index = utest_state.tests_length++; \ 976 | const char *name_part = #FIXTURE "." #NAME; \ 977 | const size_t name_size = strlen(name_part) + 1; \ 978 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 979 | utest_state.tests = UTEST_PTR_CAST( \ 980 | struct utest_test_state_s *, \ 981 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 982 | sizeof(struct utest_test_state_s) * \ 983 | utest_state.tests_length)); \ 984 | utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ 985 | utest_state.tests[index].name = name; \ 986 | UTEST_SNPRINTF(name, name_size, "%s", name_part); \ 987 | } \ 988 | UTEST_FIXTURE_SURPRESS_WARNINGS_END \ 989 | void utest_run_##FIXTURE##_##NAME(int *utest_result, \ 990 | struct FIXTURE *utest_fixture) 991 | 992 | #define UTEST_I_SETUP(FIXTURE) \ 993 | static void utest_i_setup_##FIXTURE( \ 994 | int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) 995 | 996 | #define UTEST_I_TEARDOWN(FIXTURE) \ 997 | static void utest_i_teardown_##FIXTURE( \ 998 | int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) 999 | 1000 | #define UTEST_I(FIXTURE, NAME, INDEX) \ 1001 | UTEST_EXTERN struct utest_state_s utest_state; \ 1002 | static void utest_run_##FIXTURE##_##NAME##_##INDEX(int *, struct FIXTURE *); \ 1003 | static void utest_i_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ 1004 | size_t index) { \ 1005 | struct FIXTURE fixture; \ 1006 | memset(&fixture, 0, sizeof(fixture)); \ 1007 | utest_i_setup_##FIXTURE(utest_result, &fixture, index); \ 1008 | if (UTEST_TEST_PASSED != *utest_result) { \ 1009 | return; \ 1010 | } \ 1011 | utest_run_##FIXTURE##_##NAME##_##INDEX(utest_result, &fixture); \ 1012 | utest_i_teardown_##FIXTURE(utest_result, &fixture, index); \ 1013 | } \ 1014 | UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME##_##INDEX) { \ 1015 | size_t i; \ 1016 | utest_uint64_t iUp; \ 1017 | for (i = 0; i < (INDEX); i++) { \ 1018 | const size_t index = utest_state.tests_length++; \ 1019 | const char *name_part = #FIXTURE "." #NAME; \ 1020 | const size_t name_size = strlen(name_part) + 32; \ 1021 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 1022 | utest_state.tests = UTEST_PTR_CAST( \ 1023 | struct utest_test_state_s *, \ 1024 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 1025 | sizeof(struct utest_test_state_s) * \ 1026 | utest_state.tests_length)); \ 1027 | utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ 1028 | utest_state.tests[index].index = i; \ 1029 | utest_state.tests[index].name = name; \ 1030 | iUp = UTEST_CAST(utest_uint64_t, i); \ 1031 | UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ 1032 | } \ 1033 | } \ 1034 | void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ 1035 | struct FIXTURE *utest_fixture) 1036 | 1037 | UTEST_WEAK 1038 | double utest_fabs(double d); 1039 | UTEST_WEAK 1040 | double utest_fabs(double d) { 1041 | union { 1042 | double d; 1043 | utest_uint64_t u; 1044 | } both; 1045 | both.d = d; 1046 | both.u &= 0x7fffffffffffffffu; 1047 | return both.d; 1048 | } 1049 | 1050 | UTEST_WEAK 1051 | int utest_isnan(double d); 1052 | UTEST_WEAK 1053 | int utest_isnan(double d) { 1054 | union { 1055 | double d; 1056 | utest_uint64_t u; 1057 | } both; 1058 | both.d = d; 1059 | both.u &= 0x7fffffffffffffffu; 1060 | return both.u > 0x7ff0000000000000u; 1061 | } 1062 | 1063 | UTEST_WEAK 1064 | int utest_should_filter_test(const char *filter, const char *testcase); 1065 | UTEST_WEAK int utest_should_filter_test(const char *filter, 1066 | const char *testcase) { 1067 | if (filter) { 1068 | const char *filter_cur = filter; 1069 | const char *testcase_cur = testcase; 1070 | const char *filter_wildcard = UTEST_NULL; 1071 | 1072 | while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { 1073 | if ('*' == *filter_cur) { 1074 | /* store the position of the wildcard */ 1075 | filter_wildcard = filter_cur; 1076 | 1077 | /* skip the wildcard character */ 1078 | filter_cur++; 1079 | 1080 | while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { 1081 | if ('*' == *filter_cur) { 1082 | /* 1083 | we found another wildcard (filter is something like *foo*) so we 1084 | exit the current loop, and return to the parent loop to handle 1085 | the wildcard case 1086 | */ 1087 | break; 1088 | } else if (*filter_cur != *testcase_cur) { 1089 | /* otherwise our filter didn't match, so reset it */ 1090 | filter_cur = filter_wildcard; 1091 | } 1092 | 1093 | /* move testcase along */ 1094 | testcase_cur++; 1095 | 1096 | /* move filter along */ 1097 | filter_cur++; 1098 | } 1099 | 1100 | if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { 1101 | return 0; 1102 | } 1103 | 1104 | /* if the testcase has been exhausted, we don't have a match! */ 1105 | if ('\0' == *testcase_cur) { 1106 | return 1; 1107 | } 1108 | } else { 1109 | if (*testcase_cur != *filter_cur) { 1110 | /* test case doesn't match filter */ 1111 | return 1; 1112 | } else { 1113 | /* move our filter and testcase forward */ 1114 | testcase_cur++; 1115 | filter_cur++; 1116 | } 1117 | } 1118 | } 1119 | 1120 | if (('\0' != *filter_cur) || 1121 | (('\0' != *testcase_cur) && 1122 | ((filter == filter_cur) || ('*' != filter_cur[-1])))) { 1123 | /* we have a mismatch! */ 1124 | return 1; 1125 | } 1126 | } 1127 | 1128 | return 0; 1129 | } 1130 | 1131 | static UTEST_INLINE FILE *utest_fopen(const char *filename, const char *mode) { 1132 | #ifdef _MSC_VER 1133 | FILE *file; 1134 | if (0 == fopen_s(&file, filename, mode)) { 1135 | return file; 1136 | } else { 1137 | return UTEST_NULL; 1138 | } 1139 | #else 1140 | return fopen(filename, mode); 1141 | #endif 1142 | } 1143 | 1144 | static UTEST_INLINE int utest_main(int argc, const char *const argv[]); 1145 | int utest_main(int argc, const char *const argv[]) { 1146 | utest_uint64_t failed = 0; 1147 | utest_uint64_t skipped = 0; 1148 | size_t index = 0; 1149 | size_t *failed_testcases = UTEST_NULL; 1150 | size_t failed_testcases_length = 0; 1151 | size_t *skipped_testcases = UTEST_NULL; 1152 | size_t skipped_testcases_length = 0; 1153 | const char *filter = UTEST_NULL; 1154 | utest_uint64_t ran_tests = 0; 1155 | int enable_mixed_units = 0; 1156 | int random_order = 0; 1157 | utest_uint32_t seed = 0; 1158 | 1159 | enum colours { RESET, GREEN, RED, YELLOW }; 1160 | 1161 | const int use_colours = UTEST_COLOUR_OUTPUT(); 1162 | const char *colours[] = {"\033[0m", "\033[32m", "\033[31m", "\033[33m"}; 1163 | 1164 | if (!use_colours) { 1165 | for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { 1166 | colours[index] = ""; 1167 | } 1168 | } 1169 | /* loop through all arguments looking for our options */ 1170 | for (index = 1; index < UTEST_CAST(size_t, argc); index++) { 1171 | /* Informational switches */ 1172 | const char help_str[] = "--help"; 1173 | const char list_str[] = "--list-tests"; 1174 | /* Test config switches */ 1175 | const char filter_str[] = "--filter="; 1176 | const char output_str[] = "--output="; 1177 | const char enable_mixed_units_str[] = "--enable-mixed-units"; 1178 | const char random_order_str[] = "--random-order"; 1179 | const char random_order_with_seed_str[] = "--random-order="; 1180 | 1181 | if (0 == UTEST_STRNCMP(argv[index], help_str, strlen(help_str))) { 1182 | printf("utest.h - the single file unit testing solution for C/C++!\n" 1183 | "Command line Options:\n" 1184 | " --help Show this message and exit.\n" 1185 | " --filter= Filter the test cases to run (EG. " 1186 | "MyTest*.a would run MyTestCase.a but not MyTestCase.b).\n" 1187 | " --list-tests List testnames, one per line. Output " 1188 | "names can be passed to --filter.\n"); 1189 | printf(" --output= Output an xunit XML file to the file " 1190 | "specified in .\n" 1191 | " --enable-mixed-units Enable the per-test output to contain " 1192 | "mixed units (s/ms/us/ns).\n" 1193 | " --random-order[=] Randomize the order that the tests are " 1194 | "ran in. If the optional argument is not provided, then a " 1195 | "random starting seed is used.\n"); 1196 | goto cleanup; 1197 | } else if (0 == 1198 | UTEST_STRNCMP(argv[index], filter_str, strlen(filter_str))) { 1199 | /* user wants to filter what test cases run! */ 1200 | filter = argv[index] + strlen(filter_str); 1201 | } else if (0 == 1202 | UTEST_STRNCMP(argv[index], output_str, strlen(output_str))) { 1203 | utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); 1204 | } else if (0 == UTEST_STRNCMP(argv[index], list_str, strlen(list_str))) { 1205 | for (index = 0; index < utest_state.tests_length; index++) { 1206 | UTEST_PRINTF("%s\n", utest_state.tests[index].name); 1207 | } 1208 | /* when printing the test list, don't actually run the tests */ 1209 | return 0; 1210 | } else if (0 == UTEST_STRNCMP(argv[index], enable_mixed_units_str, 1211 | strlen(enable_mixed_units_str))) { 1212 | enable_mixed_units = 1; 1213 | } else if (0 == UTEST_STRNCMP(argv[index], random_order_with_seed_str, 1214 | strlen(random_order_with_seed_str))) { 1215 | seed = 1216 | UTEST_CAST(utest_uint32_t, 1217 | strtoul(argv[index] + strlen(random_order_with_seed_str), 1218 | UTEST_NULL, 10)); 1219 | random_order = 1; 1220 | } else if (0 == UTEST_STRNCMP(argv[index], random_order_str, 1221 | strlen(random_order_str))) { 1222 | const utest_int64_t ns = utest_ns(); 1223 | 1224 | // Some really poor pseudo-random using the current time. I do this 1225 | // because I really want to avoid using C's rand() because that'd mean our 1226 | // random would be affected by any srand() usage by the user (which I 1227 | // don't want). 1228 | seed = UTEST_CAST(utest_uint32_t, ns >> 32) * 31 + 1229 | UTEST_CAST(utest_uint32_t, ns & 0xffffffff); 1230 | random_order = 1; 1231 | } 1232 | } 1233 | 1234 | if (random_order) { 1235 | // Use Fisher-Yates with the Durstenfield's version to randomly re-order the 1236 | // tests. 1237 | for (index = utest_state.tests_length; index > 1; index--) { 1238 | // For the random order we'll use PCG. 1239 | const utest_uint32_t state = seed; 1240 | const utest_uint32_t word = 1241 | ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; 1242 | const utest_uint32_t next = 1243 | ((word >> 22u) ^ word) % UTEST_CAST(utest_uint32_t, index); 1244 | 1245 | // Swap the randomly chosen element into the last location. 1246 | const struct utest_test_state_s copy = utest_state.tests[index - 1]; 1247 | utest_state.tests[index - 1] = utest_state.tests[next]; 1248 | utest_state.tests[next] = copy; 1249 | 1250 | // Move the seed onwards. 1251 | seed = seed * 747796405u + 2891336453u; 1252 | } 1253 | } 1254 | 1255 | for (index = 0; index < utest_state.tests_length; index++) { 1256 | if (utest_should_filter_test(filter, utest_state.tests[index].name)) { 1257 | continue; 1258 | } 1259 | 1260 | ran_tests++; 1261 | } 1262 | 1263 | printf("%s[==========]%s Running %" UTEST_PRIu64 " test cases.\n", 1264 | colours[GREEN], colours[RESET], UTEST_CAST(utest_uint64_t, ran_tests)); 1265 | 1266 | if (utest_state.output) { 1267 | fprintf(utest_state.output, "\n"); 1268 | fprintf(utest_state.output, 1269 | "\n", 1270 | UTEST_CAST(utest_uint64_t, ran_tests)); 1271 | fprintf(utest_state.output, 1272 | "\n", 1273 | UTEST_CAST(utest_uint64_t, ran_tests)); 1274 | } 1275 | 1276 | for (index = 0; index < utest_state.tests_length; index++) { 1277 | int result = UTEST_TEST_PASSED; 1278 | utest_int64_t ns = 0; 1279 | 1280 | if (utest_should_filter_test(filter, utest_state.tests[index].name)) { 1281 | continue; 1282 | } 1283 | 1284 | printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], 1285 | utest_state.tests[index].name); 1286 | 1287 | if (utest_state.output) { 1288 | fprintf(utest_state.output, "", 1289 | utest_state.tests[index].name); 1290 | } 1291 | 1292 | ns = utest_ns(); 1293 | errno = 0; 1294 | #if defined(__cplusplus) 1295 | UTEST_SURPRESS_WARNING_BEGIN 1296 | try { 1297 | utest_state.tests[index].func(&result, utest_state.tests[index].index); 1298 | } catch (const std::exception &err) { 1299 | printf(" Exception : %s\n", err.what()); 1300 | result = UTEST_TEST_FAILURE; 1301 | } catch (...) { 1302 | printf(" Exception : Unknown\n"); 1303 | result = UTEST_TEST_FAILURE; 1304 | } 1305 | UTEST_SURPRESS_WARNING_END 1306 | #else 1307 | utest_state.tests[index].func(&result, utest_state.tests[index].index); 1308 | #endif 1309 | ns = utest_ns() - ns; 1310 | 1311 | if (utest_state.output) { 1312 | fprintf(utest_state.output, "\n"); 1313 | } 1314 | 1315 | // Record the failing test. 1316 | if (UTEST_TEST_FAILURE == result) { 1317 | const size_t failed_testcase_index = failed_testcases_length++; 1318 | failed_testcases = UTEST_PTR_CAST( 1319 | size_t *, utest_realloc(UTEST_PTR_CAST(void *, failed_testcases), 1320 | sizeof(size_t) * failed_testcases_length)); 1321 | if (UTEST_NULL != failed_testcases) { 1322 | failed_testcases[failed_testcase_index] = index; 1323 | } 1324 | failed++; 1325 | } else if (UTEST_TEST_SKIPPED == result) { 1326 | const size_t skipped_testcase_index = skipped_testcases_length++; 1327 | skipped_testcases = UTEST_PTR_CAST( 1328 | size_t *, utest_realloc(UTEST_PTR_CAST(void *, skipped_testcases), 1329 | sizeof(size_t) * skipped_testcases_length)); 1330 | if (UTEST_NULL != skipped_testcases) { 1331 | skipped_testcases[skipped_testcase_index] = index; 1332 | } 1333 | skipped++; 1334 | } 1335 | 1336 | { 1337 | const char *const units[] = {"ns", "us", "ms", "s", UTEST_NULL}; 1338 | unsigned int unit_index = 0; 1339 | utest_int64_t time = ns; 1340 | 1341 | if (enable_mixed_units) { 1342 | for (unit_index = 0; UTEST_NULL != units[unit_index]; unit_index++) { 1343 | if (10000 > time) { 1344 | break; 1345 | } 1346 | 1347 | time /= 1000; 1348 | } 1349 | } 1350 | 1351 | if (UTEST_TEST_FAILURE == result) { 1352 | printf("%s[ FAILED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[RED], 1353 | colours[RESET], utest_state.tests[index].name, time, 1354 | units[unit_index]); 1355 | } else if (UTEST_TEST_SKIPPED == result) { 1356 | printf("%s[ SKIPPED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[YELLOW], 1357 | colours[RESET], utest_state.tests[index].name, time, 1358 | units[unit_index]); 1359 | } else { 1360 | printf("%s[ OK ]%s %s (%" UTEST_PRId64 "%s)\n", colours[GREEN], 1361 | colours[RESET], utest_state.tests[index].name, time, 1362 | units[unit_index]); 1363 | } 1364 | } 1365 | } 1366 | 1367 | printf("%s[==========]%s %" UTEST_PRIu64 " test cases ran.\n", colours[GREEN], 1368 | colours[RESET], ran_tests); 1369 | printf("%s[ PASSED ]%s %" UTEST_PRIu64 " tests.\n", colours[GREEN], 1370 | colours[RESET], ran_tests - failed - skipped); 1371 | 1372 | if (0 != skipped) { 1373 | printf("%s[ SKIPPED ]%s %" UTEST_PRIu64 " tests, listed below:\n", 1374 | colours[YELLOW], colours[RESET], skipped); 1375 | for (index = 0; index < skipped_testcases_length; index++) { 1376 | printf("%s[ SKIPPED ]%s %s\n", colours[YELLOW], colours[RESET], 1377 | utest_state.tests[skipped_testcases[index]].name); 1378 | } 1379 | } 1380 | 1381 | if (0 != failed) { 1382 | printf("%s[ FAILED ]%s %" UTEST_PRIu64 " tests, listed below:\n", 1383 | colours[RED], colours[RESET], failed); 1384 | for (index = 0; index < failed_testcases_length; index++) { 1385 | printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], 1386 | utest_state.tests[failed_testcases[index]].name); 1387 | } 1388 | } 1389 | 1390 | if (utest_state.output) { 1391 | fprintf(utest_state.output, "\n\n"); 1392 | } 1393 | 1394 | cleanup: 1395 | for (index = 0; index < utest_state.tests_length; index++) { 1396 | free(UTEST_PTR_CAST(void *, utest_state.tests[index].name)); 1397 | } 1398 | 1399 | free(UTEST_PTR_CAST(void *, skipped_testcases)); 1400 | free(UTEST_PTR_CAST(void *, failed_testcases)); 1401 | free(UTEST_PTR_CAST(void *, utest_state.tests)); 1402 | 1403 | if (utest_state.output) { 1404 | fclose(utest_state.output); 1405 | } 1406 | 1407 | return UTEST_CAST(int, failed); 1408 | } 1409 | 1410 | /* 1411 | we need, in exactly one source file, define the global struct that will hold 1412 | the data we need to run utest. This macro allows the user to declare the 1413 | data without having to use the UTEST_MAIN macro, thus allowing them to write 1414 | their own main() function. 1415 | */ 1416 | #define UTEST_STATE() struct utest_state_s utest_state = {0, 0, 0} 1417 | 1418 | /* 1419 | define a main() function to call into utest.h and start executing tests! A 1420 | user can optionally not use this macro, and instead define their own main() 1421 | function and manually call utest_main. The user must, in exactly one source 1422 | file, use the UTEST_STATE macro to declare a global struct variable that 1423 | utest requires. 1424 | */ 1425 | #define UTEST_MAIN() \ 1426 | UTEST_STATE(); \ 1427 | int main(int argc, const char *const argv[]) { \ 1428 | return utest_main(argc, argv); \ 1429 | } 1430 | 1431 | #endif /* SHEREDOM_UTEST_H_INCLUDED */ 1432 | --------------------------------------------------------------------------------