blob: 3b81cfccfa62588e654bca8034198fd3d3125244 [file] [log] [blame]
/***************************************************************************
*
* 27.ios.members.static.cpp - test exercising [lib.ios.members.static]
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright 1994-2008 Rogue Wave Software.
*
**************************************************************************/
// exercise that the default setting of ios_base::sync_with_stdio()
// produces the expected output, i.e., that the ouptut of the standard
// iostream objects, std::cout, std::cerr, etc., is properly interleaved
// with the output produced by interleaving calls to stdio printf()
// also exercises the ability of buffered standard iostream objects
// (i.e., cout, wcout, clog, and wclog) to flush output on program exit
#include <cerrno> // for errno
#include <cstdio> // for fprintf, L_tmpnam
#include <cstdlib> // for STD{ERR,OUT}_FILENO
#include <cstring> // for strerror
#include <iostream>
// rwtest headers
#include <rw_driver.h>
#include <rw_file.h>
#ifndef _WIN32
# include <fcntl.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/wait.h>
# define DEV_TTY "/dev/tty"
#else // ifdef _WIN32
# include <fcntl.h>
# include <io.h>
# ifndef STDIN_FILENO
# define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
# endif // STDIN_FILENO
# define DEV_TTY "CON:"
#endif // _WIN32
// use buffers larger than L_tmpnam with rw_tmpnam()
char stderr_fname [256]; // name of file to which stderr is redirected
char stdout_fname [256]; // name of file to which stderr is redirected
char stdio_fname [256]; // same as above but for both stderr and stdout
/**************************************************************************/
template <class charT>
/*static*/ void
test_file (const char* fname, const char* expect)
{
std::FILE* fp = std::fopen (fname, "r");
if (!fp) {
rw_assert (false, __FILE__, __LINE__,
"fopen (\"%s\", \"r\") failed: %s",
fname, std::strerror (errno));
return;
}
enum { bufsiz = 256 };
char buf [bufsiz] = { 0 };
std::fgets (buf, bufsiz, fp);
rw_assert (!std::strcmp (expect, buf), __FILE__, __LINE__,
"expected output: \"%s\", got: \"%s\"", expect, buf);
std::fclose (fp);
}
/**************************************************************************/
static void
do_test ()
{
static const char stdout_expect[] = {
"[STDOUT_FILENO]"
"[cout,1][stdout,1][cout,2][stdout,2]"
#ifndef _RWSTD_NO_WCHAR_T
"[wcout,1][stdout,3][wcout,2][stdout,4]"
#endif // _RWSTD_NO_WCHAR_T
};
static const char stderr_expect[] = {
"[STDERR_FILENO]"
"[cerr,1][stderr,1][cerr,2][stderr,2]"
"[clog,1][stderr,3][clog,2][stderr,4]"
#ifndef _RWSTD_NO_WCHAR_T
"[wcerr,1][stderr,5][wcerr,2][stderr,6]"
"[wclog,1][stderr,7][wclog,2][stderr,8]"
#endif // _RWSTD_NO_WCHAR_T
};
static const char stdio_expect[] = {
"[STDOUT_FILENO][STDERR_FILENO]"
"[cout,1][stdout,1][cout,2]" // no [stdout,2] here
"[cerr,1][stderr,1][cerr,2][stderr,2]"
"[clog,1][stderr,3][clog,2][stderr,4]"
"[stdout,2]" // but [stdout,2] flushed here
#ifndef _RWSTD_NO_WCHAR_T
"[wcout,1][stdout,3][wcout,2]" // no [stdout,2] here
"[wcerr,1][stderr,5][wcerr,2][stderr,6]"
"[wclog,1][stderr,7][wclog,2][stderr,8]"
"[stdout,4]" // but [stdout,2] flushed here
#endif // _RWSTD_NO_WCHAR_T
};
rw_info (0, __FILE__, __LINE__,
"ios_base::sync_with_stdio (true)");
// call not necessary, stdio synchronized by default
// std::ios_base::sync_with_stdio (true);
rw_info (0, __FILE__, __LINE__,
"interleaved std::cout and stdout output");
test_file<char> (stdout_fname, stdout_expect);
rw_info (0, __FILE__, __LINE__,
"interleaved std::cerr and stderr output");
test_file<char> (stderr_fname, stderr_expect);
rw_info (0, __FILE__, __LINE__,
"interleaved std::cout/cerr and stdout/stderr output");
test_file<char> (stdio_fname, stdio_expect);
}
/**************************************************************************/
static int
redirect_to_file (const char* fname, int fd)
{
static int fd_tty = open (DEV_TTY, O_WRONLY);
if (fd_tty < 0) {
std::fprintf (stderr,
"open (\"" DEV_TTY "\", O_WRONLY) failed: %s\n",
std::strerror (errno));
fd_tty = STDERR_FILENO;
}
char buf [1024];
int n;
// create a new file and redirect `fd' to it
const int fd_tmp =
open (fname, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0666);
if (fd_tmp < 0) {
n = std::sprintf (buf,
"open (\"%s\", O_WRONLY | O_CREAT | O_TRUNC, "
"0666) failed: %s\n",
fname, std::strerror (errno));
write (fd_tty, buf, n);
return -1; // bail out after a critical error
}
const int fd2 = dup2 (fd_tmp, fd);
if (0 > fd2) {
n = std::sprintf (buf,
"dup2 (%d, %d) failed: %s\n",
fd2, fd, std::strerror (errno));
write (fd_tty, buf, n);
return -2; // bail out after a critical error
}
if (fd2 != fd) {
n = std::sprintf (buf,
"dup2 (%d, %d) != %d; got %d\n",
fd_tmp, fd, fd, fd2);
// should never happen
write (fd_tty, buf, n);
}
if (close (fd_tmp)) {
n = std::sprintf (buf,
"close (%d) failed: %s\n",
fd_tmp, std::strerror (errno));
write (fd_tty, buf, n);
// proceed after a benign error
}
return fd2;
}
static int
exec_stdout_setup ()
{
// create a new file and redirect stdout to it
const int fd_stdout = redirect_to_file (stdout_fname, STDOUT_FILENO);
if (fd_stdout != STDOUT_FILENO)
return fd_stdout;
// expected output (w/o newlines):
// "[STDOUT_FILENO]"
// "[cout,1][stdout,1]"
// "[cout,2][stdout,2]"
// "[wcout,1][stdout,3]"
// "[wcout,2][stdout,4]"
write (STDOUT_FILENO, "[STDOUT_FILENO]", 15);
std::cout << "[cout,1]";
std::printf ("[stdout,1]");
std::cout << "[cout,2]"; // flushes the printf() above
std::printf ("[stdout,2]");
#ifndef _RWSTD_WCHAR_T
std::wcout << "[wcout,1]"; // flushes the printf() above
std::printf ("[stdout,3]");
std::wcout << "[wcout,2]"; // flushes the printf() above
std::printf ("[stdout,4]"); // flushed during termination
#endif // _RWSTD_WCHAR_T
return 0;
}
static int
exec_stderr_setup ()
{
// create a new file and redirect stderr to it
const int fd_stderr = redirect_to_file (stderr_fname, STDERR_FILENO);
if (fd_stderr != STDERR_FILENO)
return fd_stderr;
// expected output (w/o newlines):
// "[STDERR_FILENO]"
// "[cerr,1][stderr,1][cerr,2][stderr,2]"
// "[clog,1][stderr,3][clog,2][stderr,4]"
// "[wcerr,1][stderr,5][wcerr,2][stderr,6]"
// "[wclog,1][stderr,7][wclog,2][stderr,8]"
write (STDERR_FILENO, "[STDERR_FILENO]", 15);
std::cerr << "[cerr,1]";
std::fprintf (stderr, "[stderr,1]");
std::cerr << "[cerr,2]";
std::fprintf (stderr, "[stderr,2]");
std::clog << "[clog,1]";
std::fprintf (stderr, "[stderr,3]");
std::clog << "[clog,2]";
std::fprintf (stderr, "[stderr,4]");
#ifndef _RWSTD_WCHAR_T
std::wcerr << "[wcerr,1]";
std::fprintf (stderr, "[stderr,5]");
std::wcerr << "[wcerr,2]";
std::fprintf (stderr, "[stderr,6]");
std::wclog << "[wclog,1]";
std::fprintf (stderr, "[stderr,7]");
std::wclog << "[wclog,2]";
std::fprintf (stderr, "[stderr,8]");
#endif // _RWSTD_WCHAR_T
return 0;
}
static int
exec_stdio_setup ()
{
// create a new file and redirect both stdout and stderr to it
const int fd_stdout = redirect_to_file (stdio_fname, STDOUT_FILENO);
const int fd_stderr = redirect_to_file (stdio_fname, STDERR_FILENO);
if (fd_stdout != STDOUT_FILENO)
return fd_stdout;
if (fd_stderr != STDERR_FILENO)
return fd_stderr;
// expected output (w/o newlines):
// "[STDOUT_FILENO][STDERR_FILENO]"
// "[cout,1][stdout,1][cout,2]"
// "[cerr,1][stderr,1][cerr,2][stderr,2]"
// "[clog,1][stderr,3][clog,2][stderr,4]"
// "[stdout,2]"
// "[wcout,1][stdout,3][wcout,2]"
// "[wcerr,1][stderr,5][wcerr,2][stderr,6]"
// "[wclog,1][stderr,7][wclog,2][stderr,8]"
// "[stdout,4]"
write (STDOUT_FILENO, "[STDOUT_FILENO]", 15);
write (STDERR_FILENO, "[STDERR_FILENO]", 15);
std::cout << "[cout,1]";
std::printf ("[stdout,1]");
std::cout << "[cout,2]";
std::printf ("[stdout,2]"); // flushed after STDERR output below
std::cerr << "[cerr,1]";
std::fprintf (stderr, "[stderr,1]");
std::cerr << "[cerr,2]";
std::fprintf (stderr, "[stderr,2]");
std::clog << "[clog,1]";
std::fprintf (stderr, "[stderr,3]");
std::clog << "[clog,2]";
std::fprintf (stderr, "[stderr,4]");
#ifndef _RWSTD_NO_WCHAR_T
std::wcout << "[wcout,1]";
std::printf ("[stdout,3]");
std::wcout << "[wcout,2]";
std::printf ("[stdout,4]"); // flushed after STDERR output below
std::wcerr << "[wcerr,1]";
std::fprintf (stderr, "[stderr,5]");
std::wcerr << "[wcerr,2]";
std::fprintf (stderr, "[stderr,6]");
std::wclog << "[wclog,1]";
std::fprintf (stderr, "[stderr,7]");
std::wclog << "[wclog,2]";
std::fprintf (stderr, "[stderr,8]");
#endif // _RWSTD_NO_WCHAR_T
return 0;
}
/**************************************************************************/
struct cleanup
{
int dummy;
~cleanup () {
if (*stdout_fname)
std::remove (stdout_fname);
if (*stderr_fname)
std::remove (stderr_fname);
if (*stdio_fname)
std::remove (stdio_fname);
}
};
static int
run_test (int /* unused */, char* /* unused */ [])
{
const cleanup remove_tmp_files = { 0 };
// prevent unused warnings
(void)&remove_tmp_files;
if (!rw_tmpnam (stdout_fname)) {
std::fprintf (stderr,
"rw_tmpnam (%p) failed: %s\n",
stdout_fname, std::strerror (errno));
return 1;
}
if (!rw_tmpnam (stderr_fname)) {
std::fprintf (stderr,
"rw_tmpnam (%p) failed: %s\n",
stderr_fname, std::strerror (errno));
return 1;
}
if (!rw_tmpnam (stdio_fname)) {
std::fprintf (stderr,
"rw_tmpnam (%p) failed: %s\n",
stdio_fname, std::strerror (errno));
return 1;
}
#ifndef _WIN32
// create three child process and have each redirect
// its stdout, stderr, and both, respectively, to
// a file
// after all the children exit, the parent will check
// the contents of the file to determine whether all
// streams have been properly flushed and synchronized
for (int i = 0; i != 3; ++i) {
const pid_t child_pid = fork ();
if (child_pid < 0) { // fork error
std::fprintf (stderr, "fork() failed: %s\n",
std::strerror (errno));
return 1;
}
else if (child_pid > 0) { // parent
wait (0);
}
else { // child
int ret = 1;
switch (i) {
case 0: ret = exec_stderr_setup (); break;
case 1: ret = exec_stdout_setup (); break;
case 2: ret = exec_stdio_setup (); break;
}
// prevent child process from cleaning up files
*stdout_fname = *stderr_fname = *stdio_fname = '\0';
return ret;
}
}
#endif // _WIN32
do_test ();
return 0;
}
/* extern */ int
main (int argc, char* argv [])
{
return rw_test (argc, argv, __FILE__,
"lib.ios.members.static",
"27.4.2.4 ios_base static members",
run_test, "", 0);
}