Path: blob/main/contrib/kyua/utils/cmdline/ui_test.cpp
48199 views
// Copyright 2011 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/cmdline/ui.hpp"2930#if defined(HAVE_CONFIG_H)31# include "config.h"32#endif3334extern "C" {35#include <sys/param.h>36#include <sys/ioctl.h>3738#include <fcntl.h>39#if defined(HAVE_TERMIOS_H)40# include <termios.h>41#endif42#include <unistd.h>43}4445#include <cerrno>46#include <cstring>4748#include <atf-c++.hpp>4950#include "utils/cmdline/globals.hpp"51#include "utils/cmdline/ui_mock.hpp"52#include "utils/env.hpp"53#include "utils/format/macros.hpp"54#include "utils/optional.ipp"55#include "utils/text/table.hpp"5657namespace cmdline = utils::cmdline;58namespace text = utils::text;5960using utils::none;61using utils::optional;626364namespace {656667/// Reopens stdout as a tty and returns its width.68///69/// \return The width of the tty in columns. If the width is wider than 80, the70/// result is 5 columns narrower to match the screen_width() algorithm.71static std::size_t72reopen_stdout(void)73{74const int fd = ::open("/dev/tty", O_WRONLY);75if (fd == -1)76ATF_SKIP(F("Cannot open tty for test: %s") % ::strerror(errno));77struct ::winsize ws;78if (::ioctl(fd, TIOCGWINSZ, &ws) == -1)79ATF_SKIP(F("Cannot determine size of tty: %s") % ::strerror(errno));8081if (fd != STDOUT_FILENO) {82if (::dup2(fd, STDOUT_FILENO) == -1)83ATF_SKIP(F("Failed to redirect stdout: %s") % ::strerror(errno));84::close(fd);85}8687return ws.ws_col >= 80 ? ws.ws_col - 5 : ws.ws_col;88}899091} // anonymous namespace929394ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__columns_set__no_tty);95ATF_TEST_CASE_BODY(ui__screen_width__columns_set__no_tty)96{97utils::setenv("COLUMNS", "4321");98::close(STDOUT_FILENO);99100cmdline::ui ui;101ATF_REQUIRE_EQ(4321 - 5, ui.screen_width().get());102}103104105ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__columns_set__tty);106ATF_TEST_CASE_BODY(ui__screen_width__columns_set__tty)107{108utils::setenv("COLUMNS", "4321");109(void)reopen_stdout();110111cmdline::ui ui;112ATF_REQUIRE_EQ(4321 - 5, ui.screen_width().get());113}114115116ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__columns_empty__no_tty);117ATF_TEST_CASE_BODY(ui__screen_width__columns_empty__no_tty)118{119utils::setenv("COLUMNS", "");120::close(STDOUT_FILENO);121122cmdline::ui ui;123ATF_REQUIRE(!ui.screen_width());124}125126127ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__columns_empty__tty);128ATF_TEST_CASE_BODY(ui__screen_width__columns_empty__tty)129{130utils::setenv("COLUMNS", "");131const std::size_t columns = reopen_stdout();132133cmdline::ui ui;134ATF_REQUIRE_EQ(columns, ui.screen_width().get());135}136137138ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__columns_invalid__no_tty);139ATF_TEST_CASE_BODY(ui__screen_width__columns_invalid__no_tty)140{141utils::setenv("COLUMNS", "foo bar");142::close(STDOUT_FILENO);143144cmdline::ui ui;145ATF_REQUIRE(!ui.screen_width());146}147148149ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__columns_invalid__tty);150ATF_TEST_CASE_BODY(ui__screen_width__columns_invalid__tty)151{152utils::setenv("COLUMNS", "foo bar");153const std::size_t columns = reopen_stdout();154155cmdline::ui ui;156ATF_REQUIRE_EQ(columns, ui.screen_width().get());157}158159160ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__tty_is_file);161ATF_TEST_CASE_BODY(ui__screen_width__tty_is_file)162{163utils::unsetenv("COLUMNS");164const int fd = ::open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0755);165ATF_REQUIRE(fd != -1);166if (fd != STDOUT_FILENO) {167ATF_REQUIRE(::dup2(fd, STDOUT_FILENO) != -1);168::close(fd);169}170171cmdline::ui ui;172ATF_REQUIRE(!ui.screen_width());173}174175176ATF_TEST_CASE_WITHOUT_HEAD(ui__screen_width__cached);177ATF_TEST_CASE_BODY(ui__screen_width__cached)178{179cmdline::ui ui;180181utils::setenv("COLUMNS", "100");182ATF_REQUIRE_EQ(100 - 5, ui.screen_width().get());183184utils::setenv("COLUMNS", "80");185ATF_REQUIRE_EQ(100 - 5, ui.screen_width().get());186187utils::unsetenv("COLUMNS");188ATF_REQUIRE_EQ(100 - 5, ui.screen_width().get());189}190191192ATF_TEST_CASE_WITHOUT_HEAD(ui__err);193ATF_TEST_CASE_BODY(ui__err)194{195cmdline::ui_mock ui(10); // Keep shorter than message.196ui.err("This is a short message");197ATF_REQUIRE_EQ(1, ui.err_log().size());198ATF_REQUIRE_EQ("This is a short message", ui.err_log()[0]);199ATF_REQUIRE(ui.out_log().empty());200}201202203ATF_TEST_CASE_WITHOUT_HEAD(ui__err__tolerates_newline);204ATF_TEST_CASE_BODY(ui__err__tolerates_newline)205{206cmdline::ui_mock ui(10); // Keep shorter than message.207ui.err("This is a short message\n");208ATF_REQUIRE_EQ(1, ui.err_log().size());209ATF_REQUIRE_EQ("This is a short message\n", ui.err_log()[0]);210ATF_REQUIRE(ui.out_log().empty());211}212213214ATF_TEST_CASE_WITHOUT_HEAD(ui__out);215ATF_TEST_CASE_BODY(ui__out)216{217cmdline::ui_mock ui(10); // Keep shorter than message.218ui.out("This is a short message");219ATF_REQUIRE(ui.err_log().empty());220ATF_REQUIRE_EQ(1, ui.out_log().size());221ATF_REQUIRE_EQ("This is a short message", ui.out_log()[0]);222}223224225ATF_TEST_CASE_WITHOUT_HEAD(ui__out__tolerates_newline);226ATF_TEST_CASE_BODY(ui__out__tolerates_newline)227{228cmdline::ui_mock ui(10); // Keep shorter than message.229ui.out("This is a short message\n");230ATF_REQUIRE(ui.err_log().empty());231ATF_REQUIRE_EQ(1, ui.out_log().size());232ATF_REQUIRE_EQ("This is a short message\n", ui.out_log()[0]);233}234235236ATF_TEST_CASE_WITHOUT_HEAD(ui__out_wrap__no_refill);237ATF_TEST_CASE_BODY(ui__out_wrap__no_refill)238{239cmdline::ui_mock ui(100);240ui.out_wrap("This is a short message");241ATF_REQUIRE(ui.err_log().empty());242ATF_REQUIRE_EQ(1, ui.out_log().size());243ATF_REQUIRE_EQ("This is a short message", ui.out_log()[0]);244}245246247ATF_TEST_CASE_WITHOUT_HEAD(ui__out_wrap__refill);248ATF_TEST_CASE_BODY(ui__out_wrap__refill)249{250cmdline::ui_mock ui(16);251ui.out_wrap("This is a short message");252ATF_REQUIRE(ui.err_log().empty());253ATF_REQUIRE_EQ(2, ui.out_log().size());254ATF_REQUIRE_EQ("This is a short", ui.out_log()[0]);255ATF_REQUIRE_EQ("message", ui.out_log()[1]);256}257258259ATF_TEST_CASE_WITHOUT_HEAD(ui__out_tag_wrap__no_refill);260ATF_TEST_CASE_BODY(ui__out_tag_wrap__no_refill)261{262cmdline::ui_mock ui(100);263ui.out_tag_wrap("Some long tag: ", "This is a short message");264ATF_REQUIRE(ui.err_log().empty());265ATF_REQUIRE_EQ(1, ui.out_log().size());266ATF_REQUIRE_EQ("Some long tag: This is a short message", ui.out_log()[0]);267}268269270ATF_TEST_CASE_WITHOUT_HEAD(ui__out_tag_wrap__refill__repeat);271ATF_TEST_CASE_BODY(ui__out_tag_wrap__refill__repeat)272{273cmdline::ui_mock ui(32);274ui.out_tag_wrap("Some long tag: ", "This is a short message");275ATF_REQUIRE(ui.err_log().empty());276ATF_REQUIRE_EQ(2, ui.out_log().size());277ATF_REQUIRE_EQ("Some long tag: This is a short", ui.out_log()[0]);278ATF_REQUIRE_EQ("Some long tag: message", ui.out_log()[1]);279}280281282ATF_TEST_CASE_WITHOUT_HEAD(ui__out_tag_wrap__refill__no_repeat);283ATF_TEST_CASE_BODY(ui__out_tag_wrap__refill__no_repeat)284{285cmdline::ui_mock ui(32);286ui.out_tag_wrap("Some long tag: ", "This is a short message", false);287ATF_REQUIRE(ui.err_log().empty());288ATF_REQUIRE_EQ(2, ui.out_log().size());289ATF_REQUIRE_EQ("Some long tag: This is a short", ui.out_log()[0]);290ATF_REQUIRE_EQ(" message", ui.out_log()[1]);291}292293294ATF_TEST_CASE_WITHOUT_HEAD(ui__out_tag_wrap__tag_too_long);295ATF_TEST_CASE_BODY(ui__out_tag_wrap__tag_too_long)296{297cmdline::ui_mock ui(5);298ui.out_tag_wrap("Some long tag: ", "This is a short message");299ATF_REQUIRE(ui.err_log().empty());300ATF_REQUIRE_EQ(1, ui.out_log().size());301ATF_REQUIRE_EQ("Some long tag: This is a short message", ui.out_log()[0]);302}303304305ATF_TEST_CASE_WITHOUT_HEAD(ui__out_table__empty);306ATF_TEST_CASE_BODY(ui__out_table__empty)307{308const text::table table(3);309310text::table_formatter formatter;311formatter.set_separator(" | ");312formatter.set_column_width(0, 23);313formatter.set_column_width(1, text::table_formatter::width_refill);314315cmdline::ui_mock ui(52);316ui.out_table(table, formatter, " ");317ATF_REQUIRE(ui.out_log().empty());318}319320321ATF_TEST_CASE_WITHOUT_HEAD(ui__out_table__not_empty);322ATF_TEST_CASE_BODY(ui__out_table__not_empty)323{324text::table table(3);325{326text::table_row row;327row.push_back("First");328row.push_back("Second");329row.push_back("Third");330table.add_row(row);331}332{333text::table_row row;334row.push_back("Fourth with some text");335row.push_back("Fifth with some more text");336row.push_back("Sixth foo");337table.add_row(row);338}339340text::table_formatter formatter;341formatter.set_separator(" | ");342formatter.set_column_width(0, 23);343formatter.set_column_width(1, text::table_formatter::width_refill);344345cmdline::ui_mock ui(52);346ui.out_table(table, formatter, " ");347ATF_REQUIRE_EQ(4, ui.out_log().size());348ATF_REQUIRE_EQ(" First | Second | Third",349ui.out_log()[0]);350ATF_REQUIRE_EQ(" Fourth with some text | Fifth with | Sixth foo",351ui.out_log()[1]);352ATF_REQUIRE_EQ(" | some more | ",353ui.out_log()[2]);354ATF_REQUIRE_EQ(" | text | ",355ui.out_log()[3]);356}357358359ATF_TEST_CASE_WITHOUT_HEAD(print_error);360ATF_TEST_CASE_BODY(print_error)361{362cmdline::init("error-program");363cmdline::ui_mock ui;364cmdline::print_error(&ui, "The error.");365ATF_REQUIRE(ui.out_log().empty());366ATF_REQUIRE_EQ(1, ui.err_log().size());367ATF_REQUIRE_EQ("error-program: E: The error.", ui.err_log()[0]);368}369370371ATF_TEST_CASE_WITHOUT_HEAD(print_info);372ATF_TEST_CASE_BODY(print_info)373{374cmdline::init("info-program");375cmdline::ui_mock ui;376cmdline::print_info(&ui, "The info.");377ATF_REQUIRE(ui.out_log().empty());378ATF_REQUIRE_EQ(1, ui.err_log().size());379ATF_REQUIRE_EQ("info-program: I: The info.", ui.err_log()[0]);380}381382383ATF_TEST_CASE_WITHOUT_HEAD(print_warning);384ATF_TEST_CASE_BODY(print_warning)385{386cmdline::init("warning-program");387cmdline::ui_mock ui;388cmdline::print_warning(&ui, "The warning.");389ATF_REQUIRE(ui.out_log().empty());390ATF_REQUIRE_EQ(1, ui.err_log().size());391ATF_REQUIRE_EQ("warning-program: W: The warning.", ui.err_log()[0]);392}393394395ATF_INIT_TEST_CASES(tcs)396{397ATF_ADD_TEST_CASE(tcs, ui__screen_width__columns_set__no_tty);398ATF_ADD_TEST_CASE(tcs, ui__screen_width__columns_set__tty);399ATF_ADD_TEST_CASE(tcs, ui__screen_width__columns_empty__no_tty);400ATF_ADD_TEST_CASE(tcs, ui__screen_width__columns_empty__tty);401ATF_ADD_TEST_CASE(tcs, ui__screen_width__columns_invalid__no_tty);402ATF_ADD_TEST_CASE(tcs, ui__screen_width__columns_invalid__tty);403ATF_ADD_TEST_CASE(tcs, ui__screen_width__tty_is_file);404ATF_ADD_TEST_CASE(tcs, ui__screen_width__cached);405406ATF_ADD_TEST_CASE(tcs, ui__err);407ATF_ADD_TEST_CASE(tcs, ui__err__tolerates_newline);408ATF_ADD_TEST_CASE(tcs, ui__out);409ATF_ADD_TEST_CASE(tcs, ui__out__tolerates_newline);410411ATF_ADD_TEST_CASE(tcs, ui__out_wrap__no_refill);412ATF_ADD_TEST_CASE(tcs, ui__out_wrap__refill);413ATF_ADD_TEST_CASE(tcs, ui__out_tag_wrap__no_refill);414ATF_ADD_TEST_CASE(tcs, ui__out_tag_wrap__refill__repeat);415ATF_ADD_TEST_CASE(tcs, ui__out_tag_wrap__refill__no_repeat);416ATF_ADD_TEST_CASE(tcs, ui__out_tag_wrap__tag_too_long);417ATF_ADD_TEST_CASE(tcs, ui__out_table__empty);418ATF_ADD_TEST_CASE(tcs, ui__out_table__not_empty);419420ATF_ADD_TEST_CASE(tcs, print_error);421ATF_ADD_TEST_CASE(tcs, print_info);422ATF_ADD_TEST_CASE(tcs, print_warning);423}424425426