13f65f718SMark de Wever //===----------------------------------------------------------------------===//
2*6a54dfbfSLouis Dionne //
33f65f718SMark de Wever // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43f65f718SMark de Wever // See https://llvm.org/LICENSE.txt for license information.
53f65f718SMark de Wever // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63f65f718SMark de Wever //
73f65f718SMark de Wever //===----------------------------------------------------------------------===//
83f65f718SMark de Wever 
93f65f718SMark de Wever // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
103f65f718SMark de Wever // UNSUPPORTED: no-filesystem
113f65f718SMark de Wever // UNSUPPORTED: no-wide-characters
1204a75f54SLouis Dionne // UNSUPPORTED: libcpp-has-no-unicode
133f65f718SMark de Wever // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
143f65f718SMark de Wever 
153f65f718SMark de Wever // Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
163401b308SMark de Wever // XFAIL: clang-modules-build
173f65f718SMark de Wever 
183f65f718SMark de Wever // XFAIL: availability-fp_to_chars-missing
193f65f718SMark de Wever 
203f65f718SMark de Wever // <print>
213f65f718SMark de Wever 
223f65f718SMark de Wever // Tests the implementation of
233f65f718SMark de Wever //   void __print::__vprint_unicode_windows(FILE* __stream, string_view __fmt,
243f65f718SMark de Wever //                                          format_args __args, bool __write_nl,
253f65f718SMark de Wever //                                          bool __is_terminal);
263f65f718SMark de Wever //
273f65f718SMark de Wever // In the library when the stdout is redirected to a file it is no
283f65f718SMark de Wever // longer considered a terminal and the special terminal handling is no
293f65f718SMark de Wever // longer executed. By testing this function we can "force" emulate a
303f65f718SMark de Wever // terminal.
313f65f718SMark de Wever // Note __write_nl is tested by the public API.
323f65f718SMark de Wever 
333f65f718SMark de Wever #include <string_view>
343f65f718SMark de Wever #include <cstdio>
353f65f718SMark de Wever #include <algorithm>
363f65f718SMark de Wever #include <cassert>
373f65f718SMark de Wever 
383f65f718SMark de Wever void write_to_console(FILE*, std::wstring_view data);
393f65f718SMark de Wever #define _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION ::write_to_console
403f65f718SMark de Wever #include <print>
413f65f718SMark de Wever 
423f65f718SMark de Wever #include "test_macros.h"
433f65f718SMark de Wever #include "filesystem_test_helper.h"
443f65f718SMark de Wever #include "make_string.h"
453f65f718SMark de Wever 
463f65f718SMark de Wever TEST_GCC_DIAGNOSTIC_IGNORED("-Wuse-after-free")
473f65f718SMark de Wever 
483f65f718SMark de Wever #define SV(S) MAKE_STRING_VIEW(wchar_t, S)
493f65f718SMark de Wever 
503f65f718SMark de Wever bool calling               = false;
513f65f718SMark de Wever std::wstring_view expected = L" world";
523f65f718SMark de Wever 
533f65f718SMark de Wever void write_to_console(FILE*, std::wstring_view data) {
543f65f718SMark de Wever   assert(calling);
553f65f718SMark de Wever   assert(data == expected);
563f65f718SMark de Wever }
573f65f718SMark de Wever 
583f65f718SMark de Wever scoped_test_env env;
593f65f718SMark de Wever std::string filename = env.create_file("output.txt");
603f65f718SMark de Wever 
613f65f718SMark de Wever static void test_basics() {
623f65f718SMark de Wever   FILE* file = std::fopen(filename.c_str(), "wb");
633f65f718SMark de Wever   assert(file);
643f65f718SMark de Wever 
653f65f718SMark de Wever   // Test writing to a "non-terminal" stream does not call WriteConsoleW.
663f65f718SMark de Wever   std::__print::__vprint_unicode_windows(file, "Hello", std::make_format_args(), false, false);
673f65f718SMark de Wever   assert(std::ftell(file) == 5);
683f65f718SMark de Wever 
693f65f718SMark de Wever   // It's not possible to reliably test whether writing to a "terminal" stream
703f65f718SMark de Wever   // flushes before writing. Testing flushing a closed stream worked on some
713f65f718SMark de Wever   // platforms, but was unreliable.
723f65f718SMark de Wever   calling = true;
733f65f718SMark de Wever   std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false, true);
743f65f718SMark de Wever }
753f65f718SMark de Wever 
763f65f718SMark de Wever // When the output is a file the data is written as-is.
773f65f718SMark de Wever // When the output is a "terminal" invalid UTF-8 input is flagged.
783f65f718SMark de Wever static void test(std::wstring_view output, std::string_view input) {
793f65f718SMark de Wever   // *** File ***
803f65f718SMark de Wever   FILE* file = std::fopen(filename.c_str(), "wb");
813f65f718SMark de Wever   assert(file);
823f65f718SMark de Wever 
833f65f718SMark de Wever   std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, false);
843f65f718SMark de Wever   assert(std::ftell(file) == static_cast<long>(input.size()));
853f65f718SMark de Wever   std::fclose(file);
863f65f718SMark de Wever 
873f65f718SMark de Wever   file = std::fopen(filename.c_str(), "rb");
883f65f718SMark de Wever   assert(file);
893f65f718SMark de Wever 
903f65f718SMark de Wever   std::vector<char> buffer(input.size());
913f65f718SMark de Wever   size_t read = fread(buffer.data(), 1, buffer.size(), file);
923f65f718SMark de Wever   assert(read == input.size());
933f65f718SMark de Wever   assert(input == std::string_view(buffer.begin(), buffer.end()));
943f65f718SMark de Wever   std::fclose(file);
953f65f718SMark de Wever 
963f65f718SMark de Wever   // *** Terminal ***
973f65f718SMark de Wever   expected = output;
983f65f718SMark de Wever   std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, true);
993f65f718SMark de Wever }
1003f65f718SMark de Wever 
1013f65f718SMark de Wever static void test() {
1023f65f718SMark de Wever   // *** Test valid UTF-8 ***
1033f65f718SMark de Wever #define TEST(S) test(SV(S), S)
1043f65f718SMark de Wever   TEST("hello world");
1053f65f718SMark de Wever 
1063f65f718SMark de Wever   // copied from benchmarks/std_format_spec_string_unicode.bench.cpp
1073f65f718SMark de Wever   TEST("Lorem ipsum dolor sit amet, ne sensibus evertitur aliquando his. Iuvaret fabulas qui ex.");
1083f65f718SMark de Wever   TEST("Lōrem ipsūm dolor sīt æmeÞ, ea vel nostrud feuġǣit, muciūs tēmporiȝusrefērrēnÞur no mel.");
1093f65f718SMark de Wever   TEST("Лорем ипсум долор сит амет, еу диам тамяуам принципес вис, еяуидем цонцептам диспутандо");
1103f65f718SMark de Wever   TEST("入ト年媛ろ舗学ラロ準募ケカ社金スノ屋検れう策他セヲシ引口ぎ集7独ぱクふ出車ぽでぱ円輪ルノ受打わ。");
1113f65f718SMark de Wever   TEST("\U0001f636\u200d\U0001f32b\ufe0f");
1123f65f718SMark de Wever #undef TEST
1133f65f718SMark de Wever 
1143f65f718SMark de Wever   // *** Test invalid utf-8 ***
1153f65f718SMark de Wever   test(SV("\ufffd"), "\xc3");
1163f65f718SMark de Wever   test(SV("\ufffd("), "\xc3\x28");
1173f65f718SMark de Wever 
1183f65f718SMark de Wever   // surrogate range
1193f65f718SMark de Wever   test(SV("\ufffd"), "\xed\xa0\x80"); // U+D800
1203f65f718SMark de Wever   test(SV("\ufffd"), "\xed\xaf\xbf"); // U+DBFF
1213f65f718SMark de Wever   test(SV("\ufffd"), "\xed\xbf\x80"); // U+DC00
1223f65f718SMark de Wever   test(SV("\ufffd"), "\xed\xbf\xbf"); // U+DFFF
1233f65f718SMark de Wever 
1243f65f718SMark de Wever   // beyond valid values
1253f65f718SMark de Wever   test(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000
1263f65f718SMark de Wever   test(SV("\ufffd"), "\xf4\xbf\xbf\xbf"); // U+11FFFF
1273f65f718SMark de Wever 
1283f65f718SMark de Wever   // Validates  http://unicode.org/review/pr-121.html option 3.
1293f65f718SMark de Wever   test(SV("\u0061\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0062"), "\x61\xf1\x80\x80\xe1\x80\xc2\x62");
1303f65f718SMark de Wever }
1313f65f718SMark de Wever 
1323f65f718SMark de Wever int main(int, char**) {
1333f65f718SMark de Wever   test_basics();
1343f65f718SMark de Wever   test();
1353f65f718SMark de Wever }
136