* @brief Exhaustive fuzz test for UTF-8 case folding using all Unicode codepoints. * * - First run: Tests all valid codepoints in order (0x0 to 0x10FFFF). * - Subsequent runs: Shuffles the codepoints to create random sequences. * - Checks for both length and content matches between serial and SIMD implementations. */
| 677 | * - Checks for both length and content matches between serial and SIMD implementations. |
| 678 | */ |
| 679 | void test_utf8_case_fold_fuzz(sz_utf8_case_fold_t fold_base, sz_utf8_case_fold_t fold_simd, |
| 680 | std::size_t iterations = scale_iterations(100)) { |
| 681 | std::printf(" - testing case folding fuzz (%zu iterations + ordered check)...\n", iterations); |
| 682 | |
| 683 | // 1. Generate all valid codepoints (ordered initially) |
| 684 | std::vector<sz_rune_t> all_runes; |
| 685 | all_runes.reserve(0x10FFFF); |
| 686 | for (sz_rune_t cp = 0; cp <= 0x10FFFF; ++cp) { |
| 687 | if (cp >= 0xD800 && cp <= 0xDFFF) continue; // Skip surrogates |
| 688 | all_runes.push_back(cp); |
| 689 | } |
| 690 | |
| 691 | // 2. Prepare buffers |
| 692 | // Max UTF-8 size is 4 bytes per rune. |
| 693 | std::vector<char> input_buffer(all_runes.size() * 4); |
| 694 | // Max expansion is handled by the folding functions, usually 3x is safe for worst cases (like µ) |
| 695 | std::vector<char> output_base(input_buffer.size() * 3 + 64); |
| 696 | std::vector<char> output_simd(input_buffer.size() * 3 + 64); |
| 697 | |
| 698 | auto &rng = global_random_generator(); |
| 699 | |
| 700 | // Iterate: 0 = Ordered, 1..N = Shuffled |
| 701 | for (std::size_t it = 0; it <= iterations; ++it) { |
| 702 | if (it > 0) std::shuffle(all_runes.begin(), all_runes.end(), rng); |
| 703 | |
| 704 | // Convert to UTF-8 |
| 705 | char *data_ptr = input_buffer.data(); |
| 706 | for (sz_rune_t cp : all_runes) data_ptr += sz_rune_export(cp, (sz_u8_t *)data_ptr); |
| 707 | sz_size_t input_len = data_ptr - input_buffer.data(); |
| 708 | |
| 709 | // Run tests |
| 710 | sz_size_t len_base = fold_base(input_buffer.data(), input_len, output_base.data()); |
| 711 | sz_size_t len_simd = fold_simd(input_buffer.data(), input_len, output_simd.data()); |
| 712 | |
| 713 | // Validations |
| 714 | if (len_base != len_simd) { |
| 715 | std::fprintf(stderr, "Iteration %zu: Length mismatch base=%zu simd=%zu\n", it, len_base, len_simd); |
| 716 | assert(false); |
| 717 | } |
| 718 | |
| 719 | // Optimize: Memcmp first, then diagnose |
| 720 | if (std::memcmp(output_base.data(), output_simd.data(), len_base) != 0) { |
| 721 | std::fprintf(stderr, "Iteration %zu: Content mismatch\n", it); |
| 722 | // Find first mismatch for debug |
| 723 | for (std::size_t i = 0; i < len_base; ++i) { |
| 724 | if (output_base[i] != output_simd[i]) { |
| 725 | std::fprintf(stderr, "Mismatch at byte %zu: 0x%02X vs 0x%02X\n", i, (unsigned char)output_base[i], |
| 726 | (unsigned char)output_simd[i]); |
| 727 | |
| 728 | // Show context (16 bytes before/after) |
| 729 | sz_size_t start = (i > 16) ? i - 16 : 0; |
| 730 | sz_size_t end = (i + 16 < len_base) ? i + 16 : len_base; |
| 731 | |
| 732 | std::fprintf(stderr, "Context (Base): "); |
| 733 | for (sz_size_t j = start; j < end; ++j) |
| 734 | std::fprintf(stderr, "%02X ", (unsigned char)output_base[j]); |
| 735 | std::fprintf(stderr, "\n"); |
| 736 |
no test coverage detected
searching dependent graphs…