Implements https://wg21.link/P3142R0 Applied retroactively as DR, same as stdlibc++ and MS STL: https://github.com/orgs/microsoft/projects/1143?pane=issue&itemId=57457187
167 lines
4.8 KiB
C++
167 lines
4.8 KiB
C++
//===----------------------------------------------------------------------===//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
|
// UNSUPPORTED: no-filesystem
|
|
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
|
|
|
// XFAIL: availability-fp_to_chars-missing
|
|
|
|
// The error exception has no system error string.
|
|
// XFAIL: LIBCXX-ANDROID-FIXME
|
|
|
|
// <print>
|
|
|
|
// template<class... Args>
|
|
// void println(FILE* stream, format_string<Args...> fmt, Args&&... args);
|
|
|
|
// In the library when the stdout is redirected to a file it is no
|
|
// longer considered a terminal and the special terminal handling is no
|
|
// longer executed. There are tests in
|
|
// libcxx/test/libcxx/input.output/iostream.format/print.fun/
|
|
// to validate that behaviour
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <fstream>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <print>
|
|
#include <string_view>
|
|
|
|
#include "assert_macros.h"
|
|
#include "concat_macros.h"
|
|
#include "filesystem_test_helper.h"
|
|
#include "print_tests.h"
|
|
#include "test_format_string.h"
|
|
#include "test_macros.h"
|
|
|
|
scoped_test_env env;
|
|
std::string filename = env.create_file("output.txt");
|
|
|
|
auto test_file = []<class... Args>(std::string_view e, test_format_string<char, Args...> fmt, Args&&... args) {
|
|
std::string expected = std::string{e} + '\n';
|
|
|
|
FILE* file = fopen(filename.c_str(), "wb");
|
|
assert(file);
|
|
|
|
std::println(file, fmt, std::forward<Args>(args)...);
|
|
std::fclose(file);
|
|
|
|
std::ifstream stream{filename.c_str(), std::ios_base::in | std::ios_base::binary};
|
|
std::string out(std::istreambuf_iterator<char>{stream}, {});
|
|
TEST_REQUIRE(out == expected,
|
|
TEST_WRITE_CONCATENATED(
|
|
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
|
};
|
|
|
|
auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
|
|
// After P2216 most exceptions thrown by std::format become ill-formed.
|
|
// Therefore this tests does nothing.
|
|
// A basic ill-formed test is done in format.verify.cpp
|
|
// The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
|
|
};
|
|
|
|
// Glibc fails writing to a wide stream.
|
|
#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
|
|
static void test_wide_stream() {
|
|
FILE* file = fopen(filename.c_str(), "wb");
|
|
assert(file);
|
|
|
|
int mode = std::fwide(file, 1);
|
|
assert(mode > 0);
|
|
|
|
TEST_VALIDATE_EXCEPTION(
|
|
std::system_error,
|
|
[&]([[maybe_unused]] const std::system_error& e) {
|
|
[[maybe_unused]] std::string_view what{"failed to write formatted output"};
|
|
TEST_LIBCPP_REQUIRE(
|
|
e.what() == what,
|
|
TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
|
|
},
|
|
std::println(file, "hello"));
|
|
}
|
|
#endif // defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
|
|
|
|
static void test_read_only() {
|
|
FILE* file = fopen(filename.c_str(), "r");
|
|
assert(file);
|
|
|
|
TEST_VALIDATE_EXCEPTION(
|
|
std::system_error,
|
|
[&]([[maybe_unused]] const std::system_error& e) {
|
|
[[maybe_unused]] std::string_view what{
|
|
"failed to write formatted output: " TEST_IF_AIX("Broken pipe", "Operation not permitted")};
|
|
TEST_LIBCPP_REQUIRE(
|
|
e.what() == what,
|
|
TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
|
|
},
|
|
std::println(file, "hello"));
|
|
}
|
|
|
|
static void test_new_line() {
|
|
// Text does newline translation.
|
|
{
|
|
FILE* file = fopen(filename.c_str(), "w");
|
|
assert(file);
|
|
|
|
std::println(file, "");
|
|
#ifndef _WIN32
|
|
assert(std::ftell(file) == 1);
|
|
#else
|
|
assert(std::ftell(file) == 2);
|
|
#endif
|
|
}
|
|
// Binary no newline translation.
|
|
{
|
|
FILE* file = fopen(filename.c_str(), "wb");
|
|
assert(file);
|
|
|
|
std::println(file, "");
|
|
assert(std::ftell(file) == 1);
|
|
}
|
|
}
|
|
|
|
static void test_println_blank_line() {
|
|
// Text does newline translation.
|
|
{
|
|
FILE* file = fopen(filename.c_str(), "w");
|
|
assert(file);
|
|
|
|
std::println(file);
|
|
#ifndef _WIN32
|
|
assert(std::ftell(file) == 1);
|
|
#else
|
|
assert(std::ftell(file) == 2);
|
|
#endif
|
|
}
|
|
// Binary no newline translation.
|
|
{
|
|
FILE* file = fopen(filename.c_str(), "wb");
|
|
assert(file);
|
|
|
|
std::println(file);
|
|
assert(std::ftell(file) == 1);
|
|
}
|
|
}
|
|
|
|
int main(int, char**) {
|
|
print_tests(test_file, test_exception);
|
|
|
|
#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
|
|
test_wide_stream();
|
|
#endif
|
|
test_read_only();
|
|
test_new_line();
|
|
test_println_blank_line();
|
|
|
|
return 0;
|
|
}
|