1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10 // UNSUPPORTED: no-filesystem
11 // UNSUPPORTED: no-wide-characters
12 // UNSUPPORTED: libcpp-has-no-unicode
13 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
14 
15 // Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
16 // XFAIL: clang-modules-build
17 
18 // XFAIL: availability-fp_to_chars-missing
19 
20 // <print>
21 
22 // Tests the implementation of
23 //   void __print::__vprint_unicode_windows(FILE* __stream, string_view __fmt,
24 //                                          format_args __args, bool __write_nl,
25 //                                          bool __is_terminal);
26 //
27 // In the library when the stdout is redirected to a file it is no
28 // longer considered a terminal and the special terminal handling is no
29 // longer executed. By testing this function we can "force" emulate a
30 // terminal.
31 // Note __write_nl is tested by the public API.
32 
33 #include <string_view>
34 #include <cstdio>
35 #include <algorithm>
36 #include <cassert>
37 
38 void write_to_console(FILE*, std::wstring_view data);
39 #define _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION ::write_to_console
40 #include <print>
41 
42 #include "test_macros.h"
43 #include "filesystem_test_helper.h"
44 #include "make_string.h"
45 
46 TEST_GCC_DIAGNOSTIC_IGNORED("-Wuse-after-free")
47 
48 #define SV(S) MAKE_STRING_VIEW(wchar_t, S)
49 
50 bool calling               = false;
51 std::wstring_view expected = L" world";
52 
53 void write_to_console(FILE*, std::wstring_view data) {
54   assert(calling);
55   assert(data == expected);
56 }
57 
58 scoped_test_env env;
59 std::string filename = env.create_file("output.txt");
60 
61 static void test_basics() {
62   FILE* file = std::fopen(filename.c_str(), "wb");
63   assert(file);
64 
65   // Test writing to a "non-terminal" stream does not call WriteConsoleW.
66   std::__print::__vprint_unicode_windows(file, "Hello", std::make_format_args(), false, false);
67   assert(std::ftell(file) == 5);
68 
69   // It's not possible to reliably test whether writing to a "terminal" stream
70   // flushes before writing. Testing flushing a closed stream worked on some
71   // platforms, but was unreliable.
72   calling = true;
73   std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false, true);
74 }
75 
76 // When the output is a file the data is written as-is.
77 // When the output is a "terminal" invalid UTF-8 input is flagged.
78 static void test(std::wstring_view output, std::string_view input) {
79   // *** File ***
80   FILE* file = std::fopen(filename.c_str(), "wb");
81   assert(file);
82 
83   std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, false);
84   assert(std::ftell(file) == static_cast<long>(input.size()));
85   std::fclose(file);
86 
87   file = std::fopen(filename.c_str(), "rb");
88   assert(file);
89 
90   std::vector<char> buffer(input.size());
91   size_t read = fread(buffer.data(), 1, buffer.size(), file);
92   assert(read == input.size());
93   assert(input == std::string_view(buffer.begin(), buffer.end()));
94   std::fclose(file);
95 
96   // *** Terminal ***
97   expected = output;
98   std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, true);
99 }
100 
101 static void test() {
102   // *** Test valid UTF-8 ***
103 #define TEST(S) test(SV(S), S)
104   TEST("hello world");
105 
106   // copied from benchmarks/std_format_spec_string_unicode.bench.cpp
107   TEST("Lorem ipsum dolor sit amet, ne sensibus evertitur aliquando his. Iuvaret fabulas qui ex.");
108   TEST("Lōrem ipsūm dolor sīt æmeÞ, ea vel nostrud feuġǣit, muciūs tēmporiȝusrefērrēnÞur no mel.");
109   TEST("Лорем ипсум долор сит амет, еу диам тамяуам принципес вис, еяуидем цонцептам диспутандо");
110   TEST("入ト年媛ろ舗学ラロ準募ケカ社金スノ屋検れう策他セヲシ引口ぎ集7独ぱクふ出車ぽでぱ円輪ルノ受打わ。");
111   TEST("\U0001f636\u200d\U0001f32b\ufe0f");
112 #undef TEST
113 
114   // *** Test invalid utf-8 ***
115   test(SV("\ufffd"), "\xc3");
116   test(SV("\ufffd("), "\xc3\x28");
117 
118   // surrogate range
119   test(SV("\ufffd"), "\xed\xa0\x80"); // U+D800
120   test(SV("\ufffd"), "\xed\xaf\xbf"); // U+DBFF
121   test(SV("\ufffd"), "\xed\xbf\x80"); // U+DC00
122   test(SV("\ufffd"), "\xed\xbf\xbf"); // U+DFFF
123 
124   // beyond valid values
125   test(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000
126   test(SV("\ufffd"), "\xf4\xbf\xbf\xbf"); // U+11FFFF
127 
128   // Validates  http://unicode.org/review/pr-121.html option 3.
129   test(SV("\u0061\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0062"), "\x61\xf1\x80\x80\xe1\x80\xc2\x62");
130 }
131 
132 int main(int, char**) {
133   test_basics();
134   test();
135 }
136