Added Console class
This commit is contained in:
parent
69a413bf4b
commit
67f0f39b50
99
io/Console.cpp
Normal file
99
io/Console.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
#include "Console.h"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::ostream;
|
||||
using std::istream;
|
||||
using std::flush;
|
||||
using std::getline;
|
||||
using boost::optional;
|
||||
using boost::none;
|
||||
using std::function;
|
||||
|
||||
using namespace cpputils;
|
||||
|
||||
IOStreamConsole::IOStreamConsole(): IOStreamConsole(std::cout, std::cin) {
|
||||
}
|
||||
|
||||
IOStreamConsole::IOStreamConsole(ostream &output, istream &input): _output(output), _input(input) {
|
||||
}
|
||||
|
||||
optional<int> parseInt(const string &str) {
|
||||
try {
|
||||
string trimmed = str;
|
||||
boost::algorithm::trim(trimmed);
|
||||
int parsed = std::stoi(str);
|
||||
if (std::to_string(parsed) != trimmed) {
|
||||
return none;
|
||||
}
|
||||
return parsed;
|
||||
} catch (const std::invalid_argument &e) {
|
||||
return none;
|
||||
} catch (const std::out_of_range &e) {
|
||||
return none;
|
||||
}
|
||||
}
|
||||
|
||||
function<optional<unsigned int>(const std::string &input)> parseUIntWithMinMax(unsigned int min, unsigned int max) {
|
||||
return [min, max] (const string &input) {
|
||||
optional<int> parsed = parseInt(input);
|
||||
if (parsed == none) {
|
||||
return optional<unsigned int>(none);
|
||||
}
|
||||
unsigned int value = static_cast<unsigned int>(*parsed);
|
||||
if (value < min || value > max) {
|
||||
return optional<unsigned int>(none);
|
||||
}
|
||||
return optional<unsigned int>(value);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Return>
|
||||
Return IOStreamConsole::_askForChoice(const string &question, function<optional<Return> (const string&)> parse) {
|
||||
optional<Return> choice = none;
|
||||
do {
|
||||
_output << question << flush;
|
||||
string choiceStr;
|
||||
getline(_input, choiceStr);
|
||||
choice = parse(choiceStr);
|
||||
} while(choice == none);
|
||||
return *choice;
|
||||
}
|
||||
|
||||
unsigned int IOStreamConsole::ask(const string &question, const vector<string> &options) {
|
||||
if(options.size() == 0) {
|
||||
throw std::invalid_argument("options should have at least one entry");
|
||||
}
|
||||
_output << "\n" << question << "\n";
|
||||
for (unsigned int i = 0; i < options.size(); ++i) {
|
||||
_output << " [" << (i+1) << "] " << options[i] << "\n";
|
||||
}
|
||||
int choice = _askForChoice("Your choice [1-" + std::to_string(options.size()) + "]: ", parseUIntWithMinMax(1, options.size()));
|
||||
return choice-1;
|
||||
}
|
||||
|
||||
function<optional<bool>(const string &input)> parseYesNo() {
|
||||
return [] (const string &input) {
|
||||
string trimmed = input;
|
||||
boost::algorithm::trim(trimmed);
|
||||
if(trimmed == "Y" || trimmed == "y" || trimmed == "Yes" || trimmed == "yes") {
|
||||
return optional<bool>(true);
|
||||
} else if (trimmed == "N" || trimmed == "n" || trimmed == "No" || trimmed == "no") {
|
||||
return optional<bool>(false);
|
||||
} else {
|
||||
return optional<bool>(none);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool IOStreamConsole::askYesNo(const string &question) {
|
||||
_output << "\n" << question << "\n";
|
||||
return _askForChoice("Your choice [y/n]: ", parseYesNo());
|
||||
}
|
||||
|
||||
void IOStreamConsole::print(const std::string &output) {
|
||||
_output << output << std::flush;
|
||||
}
|
37
io/Console.h
Normal file
37
io/Console.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_IO_CONSOLE_H
|
||||
#define MESSMER_CPPUTILS_IO_CONSOLE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
class Console {
|
||||
public:
|
||||
virtual unsigned int ask(const std::string &question, const std::vector<std::string> &options) = 0;
|
||||
virtual bool askYesNo(const std::string &question) = 0;
|
||||
virtual void print(const std::string &output) = 0;
|
||||
};
|
||||
|
||||
class IOStreamConsole: public Console {
|
||||
public:
|
||||
IOStreamConsole();
|
||||
IOStreamConsole(std::ostream &output, std::istream &input);
|
||||
unsigned int ask(const std::string &question, const std::vector<std::string> &options) override;
|
||||
bool askYesNo(const std::string &question) override;
|
||||
void print(const std::string &output) override;
|
||||
private:
|
||||
template<typename Return>
|
||||
Return _askForChoice(const std::string &question, std::function<boost::optional<Return> (const std::string&)> parse);
|
||||
|
||||
std::ostream &_output;
|
||||
std::istream &_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
77
test/io/ConsoleTest.h
Normal file
77
test/io/ConsoleTest.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef MESSMER_CPPUTILS_TEST_IO_CONSOLETEST_H
|
||||
#define MESSMER_CPPUTILS_TEST_IO_CONSOLETEST_H
|
||||
|
||||
#include <google/gtest/gtest.h>
|
||||
|
||||
#include "../../io/Console.h"
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include "../../io/pipestream.h"
|
||||
|
||||
class ConsoleThread {
|
||||
public:
|
||||
ConsoleThread(std::ostream &ostr, std::istream &istr): _console(ostr, istr) {}
|
||||
std::future<unsigned int> ask(const std::string &question, const std::vector<std::string> &options) {
|
||||
return std::async(std::launch::async, [this, question, options]() {
|
||||
return _console.ask(question, options);
|
||||
});
|
||||
}
|
||||
std::future<bool> askYesNo(const std::string &question) {
|
||||
return std::async(std::launch::async, [this, question]() {
|
||||
return _console.askYesNo(question);
|
||||
});
|
||||
}
|
||||
void print(const std::string &output) {
|
||||
_console.print(output);
|
||||
}
|
||||
private:
|
||||
cpputils::IOStreamConsole _console;
|
||||
};
|
||||
|
||||
class ConsoleTest: public ::testing::Test {
|
||||
public:
|
||||
ConsoleTest(): _inputStr(), _outputStr(), _input(&_inputStr), _output(&_outputStr), _console(_output, _input) {}
|
||||
|
||||
void EXPECT_OUTPUT_LINES(std::initializer_list<std::string> lines) {
|
||||
for (const std::string &line : lines) {
|
||||
EXPECT_OUTPUT_LINE(line);
|
||||
}
|
||||
}
|
||||
|
||||
void EXPECT_OUTPUT_LINE(const std::string &expected, char delimiter = '\n', const std::string &expected_after_delimiter = "") {
|
||||
std::string actual;
|
||||
std::getline(_output, actual, delimiter);
|
||||
EXPECT_EQ(expected, actual);
|
||||
for (char expected_char : expected_after_delimiter) {
|
||||
char actual_char;
|
||||
_output.get(actual_char);
|
||||
EXPECT_EQ(expected_char, actual_char);
|
||||
}
|
||||
}
|
||||
|
||||
void sendInputLine(const std::string &line) {
|
||||
_input << line << "\n" << std::flush;
|
||||
}
|
||||
|
||||
std::future<unsigned int> ask(const std::string &question, const std::vector<std::string> &options) {
|
||||
return _console.ask(question, options);
|
||||
}
|
||||
|
||||
std::future<bool> askYesNo(const std::string &question) {
|
||||
return _console.askYesNo(question);
|
||||
}
|
||||
|
||||
void print(const std::string &output) {
|
||||
_console.print(output);
|
||||
}
|
||||
|
||||
private:
|
||||
cpputils::pipestream _inputStr;
|
||||
cpputils::pipestream _outputStr;
|
||||
std::iostream _input;
|
||||
std::iostream _output;
|
||||
ConsoleThread _console;
|
||||
};
|
||||
|
||||
#endif
|
200
test/io/ConsoleTest_Ask.cpp
Normal file
200
test/io/ConsoleTest_Ask.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
#include "ConsoleTest.h"
|
||||
|
||||
using std::stringstream;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
using std::future;
|
||||
using std::initializer_list;
|
||||
|
||||
class ConsoleTest_Ask: public ConsoleTest {};
|
||||
|
||||
TEST_F(ConsoleTest_Ask, CrashesWithoutOptions) {
|
||||
EXPECT_THROW(
|
||||
(ask("My Question?", {}).get()),
|
||||
std::invalid_argument
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, OneOption) {
|
||||
auto chosen = ask("My Question?", {"First Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] First Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-1]", ':', " ");
|
||||
sendInputLine("1");
|
||||
EXPECT_EQ(0, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, TwoOptions_ChooseFirst) {
|
||||
auto chosen = ask("My Question?", {"First Option", "Second Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] First Option",
|
||||
" [2] Second Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("1");
|
||||
EXPECT_EQ(0, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, TwoOptions_ChooseSecond) {
|
||||
auto chosen = ask("My Question?", {"First Option", "Second Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] First Option",
|
||||
" [2] Second Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("2");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseFirst) {
|
||||
auto chosen = ask("My Other Question?", {"1st Option", "2nd Option", "3rd Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Other Question?",
|
||||
" [1] 1st Option",
|
||||
" [2] 2nd Option",
|
||||
" [3] 3rd Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " ");
|
||||
sendInputLine("1");
|
||||
EXPECT_EQ(0, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseSecond) {
|
||||
auto chosen = ask("My Question?", {"1st Option", "2nd Option", "3rd Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] 1st Option",
|
||||
" [2] 2nd Option",
|
||||
" [3] 3rd Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " ");
|
||||
sendInputLine("2");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseThird) {
|
||||
auto chosen = ask("My Question?", {"1st Option", "2nd Option", "3rd Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] 1st Option",
|
||||
" [2] 2nd Option",
|
||||
" [3] 3rd Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " ");
|
||||
sendInputLine("3");
|
||||
EXPECT_EQ(2, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, InputWithLeadingSpaces) {
|
||||
auto chosen = ask("My Question?", {"First Option", "Second Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] First Option",
|
||||
" [2] Second Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine(" 2");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, InputWithFollowingSpaces) {
|
||||
auto chosen = ask("My Question?", {"First Option", "Second Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] First Option",
|
||||
" [2] Second Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("2 ");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, InputWithLeadingAndFollowingSpaces) {
|
||||
auto chosen = ask("My Question?", {"First Option", "Second Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] First Option",
|
||||
" [2] Second Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine(" 2 ");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, InputEmptyLine) {
|
||||
auto chosen = ask("My Question?", {"First Option", "Second Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] First Option",
|
||||
" [2] Second Option"
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("");
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine(" "); // empty line with space
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("2");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, InputWrongNumbers) {
|
||||
auto chosen = ask("My Question?", {"1st Option", "2nd Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] 1st Option",
|
||||
" [2] 2nd Option",
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("0");
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("-1");
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("3");
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("1.5");
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("1,5");
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("2");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_Ask, InputNonNumbers) {
|
||||
auto chosen = ask("My Question?", {"1st Option", "2nd Option"});
|
||||
EXPECT_OUTPUT_LINES({
|
||||
"",
|
||||
"My Question?",
|
||||
" [1] 1st Option",
|
||||
" [2] 2nd Option",
|
||||
});
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("abc");
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("3a"); // Wrong number and string attached
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("1a"); // Right number but string attached
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("a3"); // Wrong number and string attached
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("a1"); // Right number but string attached
|
||||
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
|
||||
sendInputLine("2");
|
||||
EXPECT_EQ(1, chosen.get());
|
||||
}
|
108
test/io/ConsoleTest_AskYesNo.cpp
Normal file
108
test/io/ConsoleTest_AskYesNo.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "ConsoleTest.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
class ConsoleTest_AskYesNo: public ConsoleTest {
|
||||
public:
|
||||
void EXPECT_TRUE_ON_INPUT(const string &input) {
|
||||
EXPECT_RESULT_ON_INPUT(true, input);
|
||||
}
|
||||
|
||||
void EXPECT_FALSE_ON_INPUT(const string &input) {
|
||||
EXPECT_RESULT_ON_INPUT(false, input);
|
||||
}
|
||||
|
||||
void EXPECT_RESULT_ON_INPUT(const bool expected, const string &input) {
|
||||
auto chosen = askYesNo("Are you sure blablub?");
|
||||
EXPECT_OUTPUT_LINES({"", "Are you sure blablub?"});
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine(input);
|
||||
EXPECT_EQ(expected, chosen.get());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_Yes) {
|
||||
EXPECT_TRUE_ON_INPUT("Yes");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_yes) {
|
||||
EXPECT_TRUE_ON_INPUT("yes");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_Y) {
|
||||
EXPECT_TRUE_ON_INPUT("Y");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_y) {
|
||||
EXPECT_TRUE_ON_INPUT("y");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_No) {
|
||||
EXPECT_FALSE_ON_INPUT("No");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_no) {
|
||||
EXPECT_FALSE_ON_INPUT("no");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_N) {
|
||||
EXPECT_FALSE_ON_INPUT("N");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, Input_n) {
|
||||
EXPECT_FALSE_ON_INPUT("n");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, InputWithLeadingSpaces) {
|
||||
EXPECT_TRUE_ON_INPUT(" y");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, InputWithFollowingSpaces) {
|
||||
EXPECT_TRUE_ON_INPUT("y ");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, InputWithLeadingAndFollowingSpaces) {
|
||||
EXPECT_TRUE_ON_INPUT(" y ");
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, InputEmptyLine) {
|
||||
auto chosen = askYesNo("My Question?");
|
||||
EXPECT_OUTPUT_LINES({"", "My Question?"});
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine(" "); // empty line with space
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("y");
|
||||
EXPECT_EQ(true, chosen.get());
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTest_AskYesNo, WrongInput) {
|
||||
auto chosen = askYesNo("My Question?");
|
||||
EXPECT_OUTPUT_LINES({"", "My Question?"});
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("0");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("1");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("bla");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("Y_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("y_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("N_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("n_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("Yes_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("yes_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("No_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("no_andsomethingelse");
|
||||
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
|
||||
sendInputLine("y");
|
||||
EXPECT_EQ(true, chosen.get());
|
||||
}
|
6
test/io/ConsoleTest_Print.cpp
Normal file
6
test/io/ConsoleTest_Print.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "ConsoleTest.h"
|
||||
|
||||
TEST_F(ConsoleTest, Print) {
|
||||
print("Bla Blub");
|
||||
EXPECT_OUTPUT_LINE("Bla Blu", 'b'); // 'b' is the delimiter for reading
|
||||
}
|
Loading…
Reference in New Issue
Block a user