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: libcpp-has-no-unicode
12// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
13
14// TODO PRINT Enable again
15// https://reviews.llvm.org/D150044
16// https://lab.llvm.org/buildbot/#/builders/237/builds/3578
17// UNSUPPORTED: asan, hwasan, msan
18
19// XFAIL: availability-fp_to_chars-missing
20
21// The error exception has no system error string.
22// XFAIL: LIBCXX-ANDROID-FIXME
23
24// <print>
25
26// void vprint_unicode(FILE* stream, string_view fmt, format_args args);
27
28// In the library when the stdout is redirected to a file it is no
29// longer considered a terminal and the special terminal handling is no
30// longer executed. There are tests in
31// libcxx/test/libcxx/input.output/iostream.format/print.fun/
32// to validate that behaviour
33
34#include <algorithm>
35#include <array>
36#include <cassert>
37#include <cstddef>
38#include <cstdio>
39#include <fstream>
40#include <iterator>
41#include <print>
42#include <string_view>
43
44#include "assert_macros.h"
45#include "concat_macros.h"
46#include "filesystem_test_helper.h"
47#include "print_tests.h"
48#include "test_macros.h"
49
50scoped_test_env env;
51std::string filename = env.create_file("output.txt");
52
53auto test_file = []<class... Args>(std::string_view expected, std::string_view fmt, Args&&... args) {
54 FILE* file = fopen(filename: filename.c_str(), modes: "wb");
55 assert(file);
56
57 std::vprint_unicode(file, fmt, std::make_format_args(args...));
58 std::fclose(stream: file);
59
60 std::ifstream stream{filename.c_str(), std::ios_base::in | std::ios_base::binary};
61 std::string out(std::istreambuf_iterator<char>{stream}, {});
62 TEST_REQUIRE(out == expected,
63 TEST_WRITE_CONCATENATED(
64 "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
65};
66
67auto test_exception = []<class... Args>([[maybe_unused]] std::string_view what,
68 [[maybe_unused]] std::string_view fmt,
69 [[maybe_unused]] Args&&... args) {
70 FILE* file = fopen(filename: filename.c_str(), modes: "wb");
71 assert(file);
72
73 TEST_VALIDATE_EXCEPTION(
74 std::format_error,
75 [&]([[maybe_unused]] const std::format_error& e) {
76 TEST_LIBCPP_REQUIRE(
77 e.what() == what,
78 TEST_WRITE_CONCATENATED(
79 "\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
80 },
81 std::vprint_unicode(file, fmt, std::make_format_args(args...)));
82
83 fclose(stream: file);
84};
85
86// Glibc fails writing to a wide stream.
87#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
88static void test_wide_stream() {
89 FILE* file = fopen(filename.c_str(), "wb");
90 assert(file);
91
92 int mode = std::fwide(file, 1);
93 assert(mode > 0);
94
95 TEST_VALIDATE_EXCEPTION(
96 std::system_error,
97 [&]([[maybe_unused]] const std::system_error& e) {
98 [[maybe_unused]] std::string_view what{"failed to write formatted output"};
99 TEST_LIBCPP_REQUIRE(
100 e.what() == what,
101 TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
102 },
103 std::vprint_unicode(file, "hello", std::make_format_args()));
104}
105#endif // defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
106
107static void test_read_only() {
108 FILE* file = fopen(filename: filename.c_str(), modes: "r");
109 assert(file);
110
111 TEST_VALIDATE_EXCEPTION(
112 std::system_error,
113 [&]([[maybe_unused]] const std::system_error& e) {
114 [[maybe_unused]] std::string_view what{
115 "failed to write formatted output: " TEST_IF_AIX("Broken pipe", "Operation not permitted")};
116 TEST_LIBCPP_REQUIRE(
117 e.what() == what,
118 TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
119 },
120 std::vprint_unicode(file, "hello", std::make_format_args()));
121}
122
123static void test_new_line() {
124 // Text does newline translation.
125 {
126 FILE* file = fopen(filename: filename.c_str(), modes: "w");
127 assert(file);
128
129 std::vprint_unicode(file, "\n", std::make_format_args());
130#ifndef _WIN32
131 assert(std::ftell(file) == 1);
132#else
133 assert(std::ftell(file) == 2);
134#endif
135 }
136 // Binary no newline translation.
137 {
138 FILE* file = fopen(filename: filename.c_str(), modes: "wb");
139 assert(file);
140
141 std::vprint_unicode(file, "\n", std::make_format_args());
142 assert(std::ftell(file) == 1);
143 }
144}
145
146int main(int, char**) {
147 print_tests(check: test_file, check_exception: test_exception);
148
149#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
150 test_wide_stream();
151#endif
152 test_read_only();
153 test_new_line();
154
155 return 0;
156}
157

source code of libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.file.pass.cpp