libstdc++: Fix std::print test case for Windows
[official-gcc.git] / libstdc++-v3 / testsuite / 27_io / print / 2.cc
blob8aa7888e7bd44b33e554b22213be427641b96698
1 // { dg-options "-lstdc++exp" }
2 // { dg-do run { target c++23 } }
3 // { dg-require-fileio "" }
5 #include <print>
6 #include <system_error>
7 #include <climits>
8 #include <cstdio>
9 #include <cstring>
10 #include <testsuite_hooks.h>
11 #include <testsuite_fs.h>
13 #ifdef _WIN32
14 #include <io.h>
15 #endif
17 namespace std
19 _GLIBCXX_BEGIN_NAMESPACE_VERSION
20 // This is an internal implementation detail that must not be used directly.
21 // We need to use it here to test the behaviour
22 error_code __write_to_terminal(void*, span<char>);
23 _GLIBCXX_END_NAMESPACE_VERSION
26 // Test the internal __write_to_terminal function that vprintf_unicode uses.
27 // The string parameter will be written to a file, then the bytes of the file
28 // will be read back again. On Windows those bytes will be a UTF-16 string.
29 // Returns true if the string was valid UTF-8.
30 bool
31 as_printed_to_terminal(std::string& s)
33 __gnu_test::scoped_file f;
34 FILE* strm = std::fopen(f.path.string().c_str(), "w");
35 VERIFY( strm );
36 #ifdef _WIN32
37 void* handle = (void*)_get_osfhandle(_fileno(strm));
38 const auto ec = std::__write_to_terminal(handle, s);
39 #else
40 const auto ec = std::__write_to_terminal(strm, s);
41 #endif
42 if (ec && ec != std::make_error_code(std::errc::illegal_byte_sequence))
44 std::println("Failed to : {}", ec.message());
45 VERIFY(!ec);
47 std::fclose(strm);
48 std::ifstream in(f.path);
49 s.assign(std::istreambuf_iterator<char>(in), {});
50 return !ec;
53 void
54 test_utf8_validation()
56 #ifndef _WIN32
57 std::string s = (const char*)u8"£🇬🇧 €🇪🇺";
58 const std::string s2 = s;
59 VERIFY( as_printed_to_terminal(s) );
60 VERIFY( s == s2 );
62 s += " \xa3 10.99 \xee \xdd";
63 const std::string s3 = s;
64 VERIFY( ! as_printed_to_terminal(s) );
65 VERIFY( s != s3 );
66 std::string repl = (const char*)u8"\uFFFD";
67 const std::string s4 = s2 + " " + repl + " 10.99 " + repl + " " + repl;
68 VERIFY( s == s4 );
70 s = "\xc0\x80";
71 VERIFY( ! as_printed_to_terminal(s) );
72 VERIFY( s == repl + repl );
73 s = "\xc0\xae";
74 VERIFY( ! as_printed_to_terminal(s) );
75 VERIFY( s == repl + repl );
77 // Examples of U+FFFD substitution from Unicode standard.
78 std::string r4 = repl + repl + repl + repl;
79 s = "\xc0\xaf\xe0\x80\xbf\xf0\x81\x82\x41"; // Table 3-8
80 VERIFY( ! as_printed_to_terminal(s) );
81 VERIFY( s == r4 + r4 + "\x41" );
82 s = "\xed\xa0\x80\xed\xbf\xbf\xed\xaf\x41"; // Table 3-9
83 VERIFY( ! as_printed_to_terminal(s) );
84 VERIFY( s == r4 + r4 + "\x41" );
85 s = "\xf4\x91\x92\x93\xff\x41\x80\xbf\x42"; // Table 3-10
86 VERIFY( ! as_printed_to_terminal(s) );
87 VERIFY( s == r4 + repl + "\x41" + repl + repl + "\x42" );
88 s = "\xe1\x80\xe2\xf0\x91\x92\xf1\xbf\x41"; // Table 3-11
89 VERIFY( ! as_printed_to_terminal(s) );
90 VERIFY( s == r4 + "\x41" );
91 #endif
94 // Create a std::u16string from the bytes in a std::string.
95 std::u16string
96 utf16_from_bytes(const std::string& s)
98 std::u16string u16;
99 // s should have an even number of bytes. If it doesn't, we'll copy its
100 // null terminator into the result, which will not match the expected value.
101 const auto len = (s.size() + 1) / 2;
102 u16.resize_and_overwrite(len, [&s](char16_t* p, size_t n) {
103 std::memcpy(p, s.data(), n * sizeof(char16_t));
104 return n;
106 return u16;
109 void
110 test_utf16_transcoding()
112 #ifdef _WIN32
113 // FIXME: We can't test __write_to_terminal for Windows, because it
114 // returns an INVALID_HANDLE Windows error when writing to a normal file.
116 std::string s = (const char*)u8"£🇬🇧 €🇪🇺";
117 const std::u16string s2 = u"£🇬🇧 €🇪🇺";
118 VERIFY( as_printed_to_terminal(s) );
119 VERIFY( utf16_from_bytes(s) == s2 );
121 s = (const char*)u8"£🇬🇧 €🇪🇺";
122 s += " \xa3 10.99 \xee\xdd";
123 VERIFY( ! as_printed_to_terminal(s) );
124 std::u16string repl = u"\uFFFD";
125 const std::u16string s3 = s2 + u" " + repl + u" 10.99 " + repl + repl;
126 VERIFY( utf16_from_bytes(s) == s3 );
128 s = "\xc0\x80";
129 VERIFY( ! as_printed_to_terminal(s) );
130 VERIFY( utf16_from_bytes(s) == repl + repl );
131 s = "\xc0\xae";
132 VERIFY( ! as_printed_to_terminal(s) );
133 VERIFY( utf16_from_bytes(s) == repl + repl );
135 // Examples of U+FFFD substitution from Unicode standard.
136 std::u16string r4 = repl + repl + repl + repl;
137 s = "\xc0\xaf\xe0\x80\xbf\xf0\x81\x82\x41"; // Table 3-8
138 VERIFY( ! as_printed_to_terminal(s) );
139 VERIFY( utf16_from_bytes(s) == r4 + r4 + u"\x41" );
140 s = "\xed\xa0\x80\xed\xbf\xbf\xed\xaf\x41"; // Table 3-9
141 VERIFY( ! as_printed_to_terminal(s) );
142 VERIFY( utf16_from_bytes(s) == r4 + r4 + u"\x41" );
143 s = "\xf4\x91\x92\x93\xff\x41\x80\xbf\x42"; // Table 3-10
144 VERIFY( ! as_printed_to_terminal(s) );
145 VERIFY( utf16_from_bytes(s) == r4 + repl + u"\x41" + repl + repl + u"\x42" );
146 s = "\xe1\x80\xe2\xf0\x91\x92\xf1\xbf\x41"; // Table 3-11
147 VERIFY( ! as_printed_to_terminal(s) );
148 VERIFY( utf16_from_bytes(s) == r4 + u"\x41" );
149 #endif
152 int main()
154 test_utf8_validation();
155 test_utf16_transcoding();