aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/getopt/ut
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/getopt/ut
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/getopt/ut')
-rw-r--r--library/cpp/getopt/ut/last_getopt_ut.cpp794
-rw-r--r--library/cpp/getopt/ut/modchooser_ut.cpp71
-rw-r--r--library/cpp/getopt/ut/opt2_ut.cpp63
-rw-r--r--library/cpp/getopt/ut/opt_ut.cpp52
-rw-r--r--library/cpp/getopt/ut/posix_getopt_ut.cpp119
-rw-r--r--library/cpp/getopt/ut/wrap.cpp96
-rw-r--r--library/cpp/getopt/ut/ya.make15
-rw-r--r--library/cpp/getopt/ut/ygetopt_ut.cpp45
8 files changed, 1255 insertions, 0 deletions
diff --git a/library/cpp/getopt/ut/last_getopt_ut.cpp b/library/cpp/getopt/ut/last_getopt_ut.cpp
new file mode 100644
index 0000000000..c99a1d053d
--- /dev/null
+++ b/library/cpp/getopt/ut/last_getopt_ut.cpp
@@ -0,0 +1,794 @@
+#include <library/cpp/getopt/last_getopt.h>
+
+#include <library/cpp/colorizer/colors.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/generic/array_size.h>
+#include <util/string/subst.h>
+#include <util/string/vector.h>
+#include <util/string/split.h>
+
+using namespace NLastGetopt;
+
+namespace {
+ struct TOptsNoDefault: public TOpts {
+ TOptsNoDefault(const TStringBuf& optstring = TStringBuf())
+ : TOpts(optstring)
+ {
+ }
+ };
+
+ class TOptsParseResultTestWrapper: public TOptsParseResultException {
+ TVector<const char*> Argv_;
+
+ public:
+ TOptsParseResultTestWrapper(const TOpts* opts, TVector<const char*> argv)
+ : Argv_(argv)
+ {
+ Init(opts, (int)Argv_.size(), Argv_.data());
+ }
+ };
+
+ using V = TVector<const char*>;
+}
+
+struct TOptsParserTester {
+ TOptsNoDefault Opts_;
+ TVector<const char*> Argv_;
+
+ THolder<TOptsParser> Parser_;
+
+ void Initialize() {
+ if (!Parser_)
+ Parser_.Reset(new TOptsParser(&Opts_, (int)Argv_.size(), Argv_.data()));
+ }
+
+ void Accept() {
+ Initialize();
+ UNIT_ASSERT(Parser_->Next());
+ }
+
+ void AcceptOption() {
+ Accept();
+ UNIT_ASSERT(!!Parser_->CurOpt());
+ }
+
+ void AcceptOption(char c) {
+ AcceptOption();
+ UNIT_ASSERT(Parser_->CurOpt()->CharIs(c));
+ }
+
+ void AcceptOption(const TString& optName) {
+ AcceptOption();
+ UNIT_ASSERT(Parser_->CurOpt()->NameIs(optName));
+ }
+
+ template <typename TOpt>
+ void AcceptOptionWithValue(TOpt optName, const TString& value) {
+ AcceptOption(optName);
+ UNIT_ASSERT_VALUES_EQUAL_C(value, Parser_->CurValStr(), "; option " << optName);
+ }
+
+ template <typename TOpt>
+ void AcceptOptionWithoutValue(TOpt optName) {
+ AcceptOption(optName);
+ UNIT_ASSERT_C(!Parser_->CurVal(), ": opt " << optName << " must have no param");
+ }
+
+ void AcceptFreeArgInOrder(const TString& expected) {
+ Accept();
+ UNIT_ASSERT(!Parser_->CurOpt());
+ UNIT_ASSERT_VALUES_EQUAL(expected, Parser_->CurValStr());
+ }
+
+ size_t Pos_;
+
+ void AcceptEndOfOptions() {
+ Initialize();
+ UNIT_ASSERT(!Parser_->Next());
+ Pos_ = Parser_->Pos_;
+
+ // pos must not be changed after last meaningful invocation of Next()
+ UNIT_ASSERT(!Parser_->Next());
+ UNIT_ASSERT_VALUES_EQUAL(Pos_, Parser_->Pos_);
+ UNIT_ASSERT(!Parser_->Next());
+ UNIT_ASSERT_VALUES_EQUAL(Pos_, Parser_->Pos_);
+ }
+
+ void AcceptError() {
+ Initialize();
+ try {
+ Parser_->Next();
+ UNIT_FAIL("expecting exception");
+ } catch (const TUsageException&) {
+ // expecting
+ }
+ }
+
+ void AcceptUnexpectedOption() {
+ Initialize();
+ size_t pos = Parser_->Pos_;
+ size_t sop = Parser_->Sop_;
+ AcceptError();
+ UNIT_ASSERT_VALUES_EQUAL(pos, Parser_->Pos_);
+ UNIT_ASSERT_VALUES_EQUAL(sop, Parser_->Sop_);
+ }
+
+ void AcceptFreeArg(const TString& expected) {
+ UNIT_ASSERT(Pos_ < Parser_->Argc_);
+ UNIT_ASSERT_VALUES_EQUAL(expected, Parser_->Argv_[Pos_]);
+ ++Pos_;
+ }
+
+ void AcceptEndOfFreeArgs() {
+ UNIT_ASSERT_VALUES_EQUAL(Argv_.size(), Pos_);
+ }
+};
+
+namespace {
+ bool gSimpleFlag = false;
+ void SimpleHander(void) {
+ gSimpleFlag = true;
+ }
+}
+
+Y_UNIT_TEST_SUITE(TLastGetoptTests) {
+ Y_UNIT_TEST(TestEqual) {
+ TOptsNoDefault opts;
+ opts.AddLongOption("from");
+ opts.AddLongOption("to");
+ TOptsParseResultTestWrapper r(&opts, V({"copy", "--from=/", "--to=/etc"}));
+
+ UNIT_ASSERT_VALUES_EQUAL("copy", r.GetProgramName());
+ UNIT_ASSERT_VALUES_EQUAL("/", r.Get("from"));
+ UNIT_ASSERT_VALUES_EQUAL("/etc", r.Get("to"));
+ UNIT_ASSERT_VALUES_EQUAL("/etc", r.GetOrElse("to", "trash"));
+ UNIT_ASSERT(r.Has("from"));
+ UNIT_ASSERT(r.Has("to"));
+
+ UNIT_ASSERT_EXCEPTION(r.Get("left"), TException);
+ }
+
+ Y_UNIT_TEST(TestCharOptions) {
+ TOptsNoDefault opts;
+ opts.AddCharOption('R', NO_ARGUMENT);
+ opts.AddCharOption('l', NO_ARGUMENT);
+ opts.AddCharOption('h', NO_ARGUMENT);
+ TOptsParseResultTestWrapper r(&opts, V({"cp", "/etc", "-Rl", "/tmp/etc"}));
+ UNIT_ASSERT(r.Has('R'));
+ UNIT_ASSERT(r.Has('l'));
+ UNIT_ASSERT(!r.Has('h'));
+
+ UNIT_ASSERT_VALUES_EQUAL(2u, r.GetFreeArgs().size());
+ UNIT_ASSERT_VALUES_EQUAL(2u, r.GetFreeArgCount());
+ UNIT_ASSERT_VALUES_EQUAL("/etc", r.GetFreeArgs()[0]);
+ UNIT_ASSERT_VALUES_EQUAL("/tmp/etc", r.GetFreeArgs()[1]);
+ }
+
+ Y_UNIT_TEST(TestFreeArgs) {
+ TOptsNoDefault opts;
+ opts.SetFreeArgsNum(1, 3);
+ TOptsParseResultTestWrapper r11(&opts, V({"cp", "/etc"}));
+ TOptsParseResultTestWrapper r12(&opts, V({"cp", "/etc", "/tmp/etc"}));
+ TOptsParseResultTestWrapper r13(&opts, V({"cp", "/etc", "/tmp/etc", "verbose"}));
+
+ UNIT_ASSERT_EXCEPTION(
+ TOptsParseResultTestWrapper(&opts, V({"cp", "/etc", "/tmp/etc", "verbose", "nosymlink"})),
+ yexception);
+
+ UNIT_ASSERT_EXCEPTION(
+ TOptsParseResultTestWrapper(&opts, V({"cp"})),
+ yexception);
+
+ opts.SetFreeArgsNum(2);
+ TOptsParseResultTestWrapper r22(&opts, V({"cp", "/etc", "/var/tmp"}));
+ }
+
+ Y_UNIT_TEST(TestCharOptionsRequiredOptional) {
+ TOptsNoDefault opts;
+ opts.AddCharOption('d', REQUIRED_ARGUMENT);
+ opts.AddCharOption('e', REQUIRED_ARGUMENT);
+ opts.AddCharOption('x', REQUIRED_ARGUMENT);
+ opts.AddCharOption('y', REQUIRED_ARGUMENT);
+ opts.AddCharOption('l', NO_ARGUMENT);
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "-ld11", "-e", "22", "-lllx33", "-y", "44"}));
+ UNIT_ASSERT_VALUES_EQUAL("11", r.Get('d'));
+ UNIT_ASSERT_VALUES_EQUAL("22", r.Get('e'));
+ UNIT_ASSERT_VALUES_EQUAL("33", r.Get('x'));
+ UNIT_ASSERT_VALUES_EQUAL("44", r.Get('y'));
+ }
+
+ Y_UNIT_TEST(TestReturnInOrder) {
+ TOptsParserTester tester;
+ tester.Opts_.AddLongOption('v', "value");
+ tester.Opts_.ArgPermutation_ = RETURN_IN_ORDER;
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("--value=11");
+ tester.Argv_.push_back("xx");
+ tester.Argv_.push_back("-v12");
+ tester.Argv_.push_back("yy");
+ tester.Argv_.push_back("--");
+ tester.Argv_.push_back("-v13");
+ tester.Argv_.push_back("--");
+
+ tester.AcceptOptionWithValue("value", "11");
+ tester.AcceptFreeArgInOrder("xx");
+ tester.AcceptOptionWithValue('v', "12");
+ tester.AcceptFreeArgInOrder("yy");
+ tester.AcceptFreeArgInOrder("-v13");
+ tester.AcceptFreeArgInOrder("--");
+ tester.AcceptEndOfOptions();
+ tester.AcceptEndOfFreeArgs();
+ }
+
+ Y_UNIT_TEST(TestRequireOrder) {
+ TOptsParserTester tester;
+ tester.Opts_.ArgPermutation_ = REQUIRE_ORDER;
+ tester.Opts_.AddLongOption('v', "value");
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("--value=11");
+ tester.Argv_.push_back("xx");
+ tester.Argv_.push_back("-v12");
+ tester.Argv_.push_back("yy");
+
+ tester.AcceptOptionWithValue("value", "11");
+ tester.AcceptEndOfOptions();
+
+ tester.AcceptFreeArg("xx");
+ tester.AcceptFreeArg("-v12");
+ tester.AcceptFreeArg("yy");
+ tester.AcceptEndOfFreeArgs();
+ }
+
+ Y_UNIT_TEST(TestPlusForLongOption) {
+ TOptsParserTester tester;
+ tester.Opts_.AddLongOption('v', "value");
+ tester.Opts_.AllowPlusForLong_ = true;
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("+value=11");
+ tester.Argv_.push_back("xx");
+ tester.Argv_.push_back("-v12");
+ tester.Argv_.push_back("yy");
+
+ tester.AcceptOptionWithValue("value", "11");
+ tester.AcceptOptionWithValue("value", "12");
+ tester.AcceptEndOfOptions();
+
+ tester.AcceptFreeArg("xx");
+ tester.AcceptFreeArg("yy");
+ tester.AcceptEndOfFreeArgs();
+ }
+
+ Y_UNIT_TEST(TestBug1) {
+ TOptsParserTester tester;
+ tester.Opts_.AddCharOptions("A:b:cd:");
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("-A");
+ tester.Argv_.push_back("aaaa");
+ tester.Argv_.push_back("zz");
+ tester.Argv_.push_back("-c");
+ tester.Argv_.push_back("-d8");
+ tester.Argv_.push_back("ww");
+
+ tester.AcceptOptionWithValue('A', "aaaa");
+ tester.AcceptOptionWithoutValue('c');
+ tester.AcceptOptionWithValue('d', "8");
+ tester.AcceptEndOfOptions();
+
+ tester.AcceptFreeArg("zz");
+ tester.AcceptFreeArg("ww");
+ tester.AcceptEndOfFreeArgs();
+ }
+
+ Y_UNIT_TEST(TestPermuteComplex) {
+ TOptsParserTester tester;
+
+ tester.Opts_.AddCharOption('x').NoArgument();
+ tester.Opts_.AddCharOption('y').RequiredArgument();
+ tester.Opts_.AddCharOption('z').NoArgument();
+ tester.Opts_.AddCharOption('w').RequiredArgument();
+ tester.Opts_.ArgPermutation_ = PERMUTE;
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("-x");
+ tester.Argv_.push_back("-y");
+ tester.Argv_.push_back("val");
+ tester.Argv_.push_back("freearg1");
+ tester.Argv_.push_back("-zw");
+ tester.Argv_.push_back("val2");
+ tester.Argv_.push_back("freearg2");
+
+ tester.AcceptOptionWithoutValue('x');
+ tester.AcceptOptionWithValue('y', "val");
+ tester.AcceptOptionWithoutValue('z');
+ tester.AcceptOptionWithValue('w', "val2");
+ tester.AcceptEndOfOptions();
+ tester.AcceptFreeArg("freearg1");
+ tester.AcceptFreeArg("freearg2");
+ tester.AcceptEndOfFreeArgs();
+ }
+
+ Y_UNIT_TEST(TestFinalDashDash) {
+ TOptsParserTester tester;
+ tester.Opts_.AddLongOption("size");
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("--");
+
+ tester.AcceptEndOfOptions();
+ tester.AcceptEndOfFreeArgs();
+ }
+
+ Y_UNIT_TEST(TestDashDashAfterDashDash) {
+ TOptsParserTester tester;
+ tester.Opts_.AddLongOption("size");
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("--");
+ tester.Argv_.push_back("--");
+ tester.Argv_.push_back("--");
+
+ tester.AcceptEndOfOptions();
+ tester.AcceptFreeArg("--");
+ tester.AcceptFreeArg("--");
+ tester.AcceptEndOfFreeArgs();
+ }
+
+ Y_UNIT_TEST(TestUnexpectedUnknownOption) {
+ TOptsParserTester tester;
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("-x");
+
+ tester.AcceptUnexpectedOption();
+ }
+
+ Y_UNIT_TEST(TestDuplicatedOptionCrash) {
+ // this test is broken, cause UNIT_ASSERT(false) always throws
+ return;
+
+ bool exception = false;
+ try {
+ TOpts opts;
+ opts.AddLongOption('x', "one");
+ opts.AddLongOption('x', "two");
+ UNIT_ASSERT(false);
+ } catch (...) {
+ // we should go here, duplicating options are forbidden
+ exception = true;
+ }
+ UNIT_ASSERT(exception);
+ }
+
+ Y_UNIT_TEST(TestPositionWhenNoArgs) {
+ TOptsParserTester tester;
+
+ tester.Argv_.push_back("cmd");
+
+ tester.Opts_.AddCharOption('c');
+
+ tester.AcceptEndOfOptions();
+
+ UNIT_ASSERT_VALUES_EQUAL(1u, tester.Parser_->Pos_);
+ }
+
+ Y_UNIT_TEST(TestExpectedUnknownCharOption) {
+ TOptsParserTester tester;
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("-x");
+ tester.Argv_.push_back("-y");
+ tester.Argv_.push_back("val");
+ tester.Argv_.push_back("freearg1");
+ tester.Argv_.push_back("-zw");
+ tester.Argv_.push_back("val2");
+ tester.Argv_.push_back("freearg2");
+
+ tester.Opts_.AllowUnknownCharOptions_ = true;
+
+ tester.AcceptOptionWithoutValue('x');
+ tester.AcceptOptionWithValue('y', "val");
+ tester.AcceptOptionWithoutValue('z');
+ tester.AcceptOptionWithValue('w', "val2");
+ tester.AcceptEndOfOptions();
+ tester.AcceptFreeArg("freearg1");
+ tester.AcceptFreeArg("freearg2");
+ tester.AcceptEndOfFreeArgs();
+ }
+
+#if 0
+ Y_UNIT_TEST(TestRequiredParams) {
+ TOptsParserTester tester;
+
+ tester.Argv_.push_back("cmd");
+ tester.Argv_.push_back("--port=1231");
+ tester.Argv_.push_back("asas");
+
+ tester.Opts_.AddLongOption("port");
+ tester.Opts_.AddLongOption("home").Required();
+
+ tester.AcceptOptionWithValue("port", "1231");
+ tester.AcceptError();
+ }
+#endif
+
+ Y_UNIT_TEST(TestStoreResult) {
+ TOptsNoDefault opts;
+ TString data;
+ int number;
+ TMaybe<TString> optionalString0, optionalString1;
+ TMaybe<int> optionalNumber0, optionalNumber1;
+ opts.AddLongOption('d', "data").StoreResult(&data);
+ opts.AddLongOption('n', "number").StoreResult(&number);
+ opts.AddLongOption("optional-string-0").StoreResult(&optionalString0);
+ opts.AddLongOption("optional-number-0").StoreResult(&optionalNumber0);
+ opts.AddLongOption("optional-string-1").StoreResult(&optionalString1);
+ opts.AddLongOption("optional-number-1").StoreResult(&optionalNumber1);
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--data=jjhh", "-n", "11", "--optional-number-1=8", "--optional-string-1=os1"}));
+ UNIT_ASSERT_VALUES_EQUAL("jjhh", data);
+ UNIT_ASSERT_VALUES_EQUAL(11, number);
+ UNIT_ASSERT(!optionalString0.Defined());
+ UNIT_ASSERT(!optionalNumber0.Defined());
+ UNIT_ASSERT_VALUES_EQUAL(*optionalString1, "os1");
+ UNIT_ASSERT_VALUES_EQUAL(*optionalNumber1, 8);
+ }
+
+ Y_UNIT_TEST(TestStoreValue) {
+ int a = 0, b = 0;
+ size_t c = 0;
+ EHasArg e = NO_ARGUMENT;
+
+ TOptsNoDefault opts;
+ opts.AddLongOption('a', "alpha").NoArgument().StoreValue(&a, 42);
+ opts.AddLongOption('b', "beta").NoArgument().StoreValue(&b, 24);
+ opts.AddLongOption('e', "enum").NoArgument().StoreValue(&e, REQUIRED_ARGUMENT).StoreValue(&c, 12345);
+
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "-a", "-e"}));
+
+ UNIT_ASSERT_VALUES_EQUAL(42, a);
+ UNIT_ASSERT_VALUES_EQUAL(0, b);
+ UNIT_ASSERT(e == REQUIRED_ARGUMENT);
+ UNIT_ASSERT_VALUES_EQUAL(12345u, c);
+ }
+
+ Y_UNIT_TEST(TestSetFlag) {
+ bool a = false, b = true, c = false, d = true;
+
+ TOptsNoDefault opts;
+ opts.AddLongOption('a', "alpha").NoArgument().SetFlag(&a);
+ opts.AddLongOption('b', "beta").NoArgument().SetFlag(&b);
+ opts.AddCharOption('c').StoreTrue(&c);
+ opts.AddCharOption('d').StoreTrue(&d);
+
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "-a", "-c"}));
+
+ UNIT_ASSERT(a);
+ UNIT_ASSERT(!b);
+ UNIT_ASSERT(c);
+ UNIT_ASSERT(!d);
+ }
+
+ Y_UNIT_TEST(TestDefaultValue) {
+ TOptsNoDefault opts;
+ opts.AddLongOption("path").DefaultValue("/etc");
+ int value = 42;
+ opts.AddLongOption("value").StoreResult(&value).DefaultValue(32);
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "dfdf"}));
+ UNIT_ASSERT_VALUES_EQUAL("/etc", r.Get("path"));
+ UNIT_ASSERT_VALUES_EQUAL(32, value);
+ }
+
+ Y_UNIT_TEST(TestSplitValue) {
+ TOptsNoDefault opts;
+ TVector<TString> vals;
+ opts.AddLongOption('s', "split").SplitHandler(&vals, ',');
+ TOptsParseResultTestWrapper r(&opts, V({"prog", "--split=a,b,c"}));
+ UNIT_ASSERT_EQUAL(vals.size(), 3);
+ UNIT_ASSERT_EQUAL(vals[0], "a");
+ UNIT_ASSERT_EQUAL(vals[1], "b");
+ UNIT_ASSERT_EQUAL(vals[2], "c");
+ }
+
+ Y_UNIT_TEST(TestRangeSplitValue) {
+ TOptsNoDefault opts;
+ TVector<ui32> vals;
+ opts.AddLongOption('s', "split").RangeSplitHandler(&vals, ',', '-');
+ TOptsParseResultTestWrapper r(&opts, V({"prog", "--split=1,8-10", "--split=12-14"}));
+ UNIT_ASSERT_EQUAL(vals.size(), 7);
+ UNIT_ASSERT_EQUAL(vals[0], 1);
+ UNIT_ASSERT_EQUAL(vals[1], 8);
+ UNIT_ASSERT_EQUAL(vals[2], 9);
+ UNIT_ASSERT_EQUAL(vals[3], 10);
+ UNIT_ASSERT_EQUAL(vals[4], 12);
+ UNIT_ASSERT_EQUAL(vals[5], 13);
+ UNIT_ASSERT_EQUAL(vals[6], 14);
+ }
+
+ Y_UNIT_TEST(TestParseArgs) {
+ TOptsNoDefault o("AbCx:y:z::");
+ UNIT_ASSERT_EQUAL(o.GetCharOption('A').HasArg_, NO_ARGUMENT);
+ UNIT_ASSERT_EQUAL(o.GetCharOption('b').HasArg_, NO_ARGUMENT);
+ UNIT_ASSERT_EQUAL(o.GetCharOption('C').HasArg_, NO_ARGUMENT);
+ UNIT_ASSERT_EQUAL(o.GetCharOption('x').HasArg_, REQUIRED_ARGUMENT);
+ UNIT_ASSERT_EQUAL(o.GetCharOption('y').HasArg_, REQUIRED_ARGUMENT);
+ UNIT_ASSERT_EQUAL(o.GetCharOption('z').HasArg_, OPTIONAL_ARGUMENT);
+ }
+
+ Y_UNIT_TEST(TestRequiredOpts) {
+ TOptsNoDefault opts;
+ TOpt& opt_d = opts.AddCharOption('d');
+
+ // test 'not required'
+ // makes sure that the problem will only be in 'required'
+ TOptsParseResultTestWrapper r1(&opts, V({"cmd"}));
+
+ // test 'required'
+ opt_d.Required();
+ UNIT_ASSERT_EXCEPTION(
+ TOptsParseResultTestWrapper(&opts, V({"cmd"})),
+ TUsageException);
+
+ TOptsParseResultTestWrapper r3(&opts, V({"cmd", "-d11"}));
+ UNIT_ASSERT_VALUES_EQUAL("11", r3.Get('d'));
+ }
+
+ class HandlerStoreTrue {
+ bool* Flag;
+
+ public:
+ HandlerStoreTrue(bool* flag)
+ : Flag(flag)
+ {
+ }
+ void operator()() {
+ *Flag = true;
+ }
+ };
+ Y_UNIT_TEST(TestHandlers) {
+ {
+ TOptsNoDefault opts;
+ bool flag = false;
+ opts.AddLongOption("flag").Handler0(HandlerStoreTrue(&flag)).NoArgument();
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--flag"}));
+ UNIT_ASSERT(flag);
+ }
+ {
+ TOptsNoDefault opts;
+ unsigned uval = 5;
+ double fval = 0.0;
+ opts.AddLongOption("flag1").RequiredArgument().StoreResult(&uval);
+ opts.AddLongOption("flag2").RequiredArgument().StoreResultT<int>(&uval);
+ opts.AddLongOption("flag3").RequiredArgument().StoreMappedResult(&fval, (double (*)(double))fabs);
+ opts.AddLongOption("flag4").RequiredArgument().StoreMappedResult(&fval, (double (*)(double))sqrt);
+ UNIT_ASSERT_EXCEPTION(
+ TOptsParseResultTestWrapper(&opts, V({"cmd", "--flag3", "-2.0", "--flag1", "-1"})),
+ yexception);
+ UNIT_ASSERT_VALUES_EQUAL(uval, 5u);
+ UNIT_ASSERT_VALUES_EQUAL(fval, 2.0);
+ TOptsParseResultTestWrapper r1(&opts, V({"cmd", "--flag4", "9.0", "--flag2", "-1"}));
+ UNIT_ASSERT_VALUES_EQUAL(uval, Max<unsigned>());
+ UNIT_ASSERT_VALUES_EQUAL(fval, 3.0);
+ }
+ }
+
+ Y_UNIT_TEST(TestTitleAndPrintUsage) {
+ TOpts opts;
+ const char* prog = "my_program";
+ TString title = TString("Sample ") + TString(prog).Quote() + " application";
+ opts.SetTitle(title);
+ int argc = 2;
+ const char* cmd[] = {prog};
+ TOptsParser parser(&opts, argc, cmd);
+ TStringStream out;
+ parser.PrintUsage(out);
+ // find title
+ UNIT_ASSERT(out.Str().find(title) != TString::npos);
+ // find usage
+ UNIT_ASSERT(out.Str().find(" " + TString(prog) + " ") != TString::npos);
+ }
+
+ Y_UNIT_TEST(TestCustomCmdLineDescr) {
+ TOpts opts;
+ const char* prog = "my_program";
+ TString customDescr = "<FILE|TABLE> USER [OPTIONS]";
+ int argc = 2;
+ const char* cmd[] = {prog};
+ opts.SetCmdLineDescr(customDescr);
+ TOptsParser parser(&opts, argc, cmd);
+ TStringStream out;
+ parser.PrintUsage(out);
+ // find custom usage
+ UNIT_ASSERT(out.Str().find(customDescr) != TString::npos);
+ }
+
+ Y_UNIT_TEST(TestColorPrint) {
+ TOpts opts;
+ const char* prog = "my_program";
+ opts.AddLongOption("long_option").Required();
+ opts.AddLongOption('o', "other");
+ opts.AddCharOption('d').DefaultValue("42");
+ opts.AddCharOption('s').DefaultValue("str_default");
+ opts.SetFreeArgsNum(123, 456);
+ opts.SetFreeArgTitle(0, "first_free_arg", "help");
+ opts.SetFreeArgTitle(2, "second_free_arg");
+ opts.AddSection("Section", "Section\n text");
+ const char* cmd[] = {prog};
+ TOptsParser parser(&opts, Y_ARRAY_SIZE(cmd), cmd);
+ TStringStream out;
+ NColorizer::TColors colors(true);
+ parser.PrintUsage(out, colors);
+
+ // find options and green color
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "--long_option" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "--other" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "-o" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "-d" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "-s" << colors.OldColor()) != TString::npos);
+
+ // find default values
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.CyanColor() << "42" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.CyanColor() << "\"str_default\"" << colors.OldColor()) != TString::npos);
+
+ // find free args
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "123" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "456" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "first_free_arg" << colors.OldColor()) != TString::npos);
+ // free args without help not rendered even if they have custom title
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "second_free_arg" << colors.OldColor()) == TString::npos);
+
+ // find signatures
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Usage" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Required parameters" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Optional parameters" << colors.OldColor()) != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Free args" << colors.OldColor()) != TString::npos);
+
+ // find sections
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Section" << colors.OldColor() << ":") != TString::npos);
+ UNIT_ASSERT(out.Str().find(TStringBuilder() << " Section\n text") != TString::npos);
+
+ // print without colors
+ TStringStream out2;
+ opts.PrintUsage(prog, out2);
+ UNIT_ASSERT(out2.Str().find(colors.GreenColor()) == TString::npos);
+ UNIT_ASSERT(out2.Str().find(colors.CyanColor()) == TString::npos);
+ UNIT_ASSERT(out2.Str().find(colors.BoldColor()) == TString::npos);
+ UNIT_ASSERT(out2.Str().find(colors.OldColor()) == TString::npos);
+ }
+
+ Y_UNIT_TEST(TestPadding) {
+ const bool withColorsOpt[] = {false, true};
+ for (bool withColors : withColorsOpt) {
+ TOpts opts;
+ const char* prog = "my_program";
+ opts.AddLongOption("option", "description 1").Required(); // long option
+ opts.AddLongOption('o', "other", "description 2"); // char and long option
+ opts.AddCharOption('d', "description 3").RequiredArgument("DD"); // char option
+ opts.AddCharOption('s', "description 4\ndescription 5\ndescription 6"); // multiline desc
+ opts.AddLongOption('l', "very_very_very_loooong_ooooption", "description 7").RequiredArgument("LONG_ARGUMENT");
+ const char* cmd[] = {prog};
+ TOptsParser parser(&opts, Y_ARRAY_SIZE(cmd), cmd);
+
+ TStringStream out;
+ NColorizer::TColors colors(withColors);
+ parser.PrintUsage(out, colors);
+
+ TString printed = out.Str();
+ if (withColors) {
+ // remove not printable characters
+ SubstGlobal(printed, TString(colors.BoldColor()), "");
+ SubstGlobal(printed, TString(colors.GreenColor()), "");
+ SubstGlobal(printed, TString(colors.CyanColor()), "");
+ SubstGlobal(printed, TString(colors.OldColor()), "");
+ }
+ TVector<TString> lines;
+ StringSplitter(printed).Split('\n').SkipEmpty().Collect(&lines);
+ UNIT_ASSERT(!lines.empty());
+ TVector<size_t> indents;
+ for (const TString& line : lines) {
+ const size_t indent = line.find("description ");
+ if (indent != TString::npos)
+ indents.push_back(indent);
+ }
+ UNIT_ASSERT_VALUES_EQUAL(indents.size(), 7);
+ const size_t theOnlyIndent = indents[0];
+ for (size_t indent : indents) {
+ UNIT_ASSERT_VALUES_EQUAL_C(indent, theOnlyIndent, printed);
+ }
+ }
+ }
+
+ Y_UNIT_TEST(TestAppendTo) {
+ TVector<int> ints;
+
+ TOptsNoDefault opts;
+ opts.AddLongOption("size").AppendTo(&ints);
+
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--size=17", "--size=19"}));
+
+ UNIT_ASSERT_VALUES_EQUAL(size_t(2), ints.size());
+ UNIT_ASSERT_VALUES_EQUAL(17, ints.at(0));
+ UNIT_ASSERT_VALUES_EQUAL(19, ints.at(1));
+ }
+
+ Y_UNIT_TEST(TestEmplaceTo) {
+ TVector<std::tuple<TString>> richPaths;
+
+ TOptsNoDefault opts;
+ opts.AddLongOption("path").EmplaceTo(&richPaths);
+
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--path=<a=b>//cool", "--path=//nice"}));
+
+ UNIT_ASSERT_VALUES_EQUAL(size_t(2), richPaths.size());
+ UNIT_ASSERT_VALUES_EQUAL("<a=b>//cool", std::get<0>(richPaths.at(0)));
+ UNIT_ASSERT_VALUES_EQUAL("//nice", std::get<0>(richPaths.at(1)));
+ }
+
+ Y_UNIT_TEST(TestKVHandler) {
+ TStringBuilder keyvals;
+
+ TOptsNoDefault opts;
+ opts.AddLongOption("set").KVHandler([&keyvals](TString k, TString v) { keyvals << k << ":" << v << ","; });
+
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--set", "x=1", "--set", "y=2", "--set=z=3"}));
+
+ UNIT_ASSERT_VALUES_EQUAL(keyvals, "x:1,y:2,z:3,");
+ }
+
+ Y_UNIT_TEST(TestEasySetup) {
+ TEasySetup opts;
+ bool flag = false;
+ opts('v', "version", "print version information")('a', "abstract", "some abstract param", true)('b', "buffer", "SIZE", "some param with argument")('c', "count", "SIZE", "some param with required argument")('t', "true", HandlerStoreTrue(&flag), "Some arg with handler")("global", SimpleHander, "Another arg with handler");
+
+ {
+ gSimpleFlag = false;
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--abstract"}));
+ UNIT_ASSERT(!flag);
+ UNIT_ASSERT(!gSimpleFlag);
+ }
+
+ {
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--abstract", "--global", "-t"}));
+ UNIT_ASSERT(flag);
+ UNIT_ASSERT(gSimpleFlag);
+ }
+
+ {
+ UNIT_ASSERT_EXCEPTION(
+ TOptsParseResultTestWrapper(&opts, V({"cmd", "--true"})),
+ TUsageException);
+ }
+
+ {
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "--abstract", "--buffer=512"}));
+ UNIT_ASSERT(r.Has('b'));
+ UNIT_ASSERT_VALUES_EQUAL(r.Get('b', 0), "512");
+ }
+ }
+
+ Y_UNIT_TEST(TestTOptsParseResultException) {
+ // verify that TOptsParseResultException actually throws a TUsageException instead of exit()
+ // not using wrapper here because it can hide bugs (see review #243810 and r2737774)
+ TOptsNoDefault opts;
+ opts.AddLongOption("required-opt").Required();
+ const char* argv[] = {"cmd"};
+ // Should throw TUsageException. Other exception types, no exceptions at all and exit(1) are failures
+ UNIT_ASSERT_EXCEPTION(
+ TOptsParseResultException(&opts, Y_ARRAY_SIZE(argv), argv),
+ TUsageException);
+ }
+
+ Y_UNIT_TEST(TestFreeArgsStoreResult) {
+ TOptsNoDefault opts;
+ TString data;
+ int number = 0;
+ opts.AddFreeArgBinding("data", data);
+ opts.AddFreeArgBinding("number", number);
+ TOptsParseResultTestWrapper r(&opts, V({"cmd", "hello", "25"}));
+ UNIT_ASSERT_VALUES_EQUAL("hello", data);
+ UNIT_ASSERT_VALUES_EQUAL(25, number);
+ UNIT_ASSERT_VALUES_EQUAL(2, r.GetFreeArgCount());
+ }
+}
diff --git a/library/cpp/getopt/ut/modchooser_ut.cpp b/library/cpp/getopt/ut/modchooser_ut.cpp
new file mode 100644
index 0000000000..a14c8a5853
--- /dev/null
+++ b/library/cpp/getopt/ut/modchooser_ut.cpp
@@ -0,0 +1,71 @@
+#include <library/cpp/getopt/modchooser.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/stream/str.h>
+
+void ValidateArgcArgv(int argc, const char** argv) {
+ UNIT_ASSERT_EQUAL(argc, 1);
+ UNIT_ASSERT_EQUAL(argv[argc], nullptr);
+}
+
+int One(int argc, const char** argv) {
+ ValidateArgcArgv(argc, argv);
+ return 1;
+}
+
+int Two(int argc, const char** argv) {
+ ValidateArgcArgv(argc, argv);
+ return 2;
+}
+
+int Three(int argc, const char** argv) {
+ ValidateArgcArgv(argc, argv);
+ return 3;
+}
+
+int Four(int argc, const char** argv) {
+ ValidateArgcArgv(argc, argv);
+ return 4;
+}
+
+int Five(int argc, const char** argv) {
+ ValidateArgcArgv(argc, argv);
+ return 5;
+}
+
+typedef int (*F_PTR)(int, const char**);
+static const F_PTR FUNCTIONS[] = {One, Two, Three, Four, Five};
+static const char* NAMES[] = {"one", "two", "three", "four", "five"};
+static_assert(Y_ARRAY_SIZE(FUNCTIONS) == Y_ARRAY_SIZE(NAMES), "Incorrect input tests data");
+
+Y_UNIT_TEST_SUITE(TModChooserTest) {
+ Y_UNIT_TEST(TestModesSimpleRunner) {
+ TModChooser chooser;
+ for (size_t idx = 0; idx < Y_ARRAY_SIZE(NAMES); ++idx) {
+ chooser.AddMode(NAMES[idx], FUNCTIONS[idx], NAMES[idx]);
+ }
+
+ // test argc, argv
+ for (size_t idx = 0; idx < Y_ARRAY_SIZE(NAMES); ++idx) {
+ int argc = 2;
+ const char* argv[] = {"UNITTEST", NAMES[idx], nullptr};
+ UNIT_ASSERT_EQUAL(static_cast<int>(idx) + 1, chooser.Run(argc, argv));
+ }
+
+ // test TVector<TString> argv
+ for (size_t idx = 0; idx < Y_ARRAY_SIZE(NAMES); ++idx) {
+ const TVector<TString> argv = {"UNITTEST", NAMES[idx]};
+ UNIT_ASSERT_EQUAL(static_cast<int>(idx) + 1, chooser.Run(argv));
+ }
+ }
+
+ Y_UNIT_TEST(TestHelpMessage) {
+ TModChooser chooser;
+
+ int argc = 2;
+ const char* argv[] = {"UNITTEST", "-?", nullptr};
+
+ chooser.Run(argc, argv);
+ }
+}
diff --git a/library/cpp/getopt/ut/opt2_ut.cpp b/library/cpp/getopt/ut/opt2_ut.cpp
new file mode 100644
index 0000000000..0e7464747c
--- /dev/null
+++ b/library/cpp/getopt/ut/opt2_ut.cpp
@@ -0,0 +1,63 @@
+#include <library/cpp/getopt/opt2.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+//using namespace NLastGetopt;
+
+Y_UNIT_TEST_SUITE(Opt2Test) {
+ Y_UNIT_TEST(TestSimple) {
+ int argc = 8;
+ char* argv[] = {
+ (char*)"cmd",
+ (char*)"--aaaa=aaaa",
+ (char*)"zz",
+ (char*)"-x1",
+ (char*)"-x2",
+ (char*)"-c",
+ (char*)"-d8",
+ (char*)"ww",
+ };
+
+ Opt2 opt(argc, argv, "A:b:cd:e:x:", 2, "aaaa=A");
+
+ const char* edef = "edef";
+ const char* a = opt.Arg('A', "<var_name> - usage of -A");
+ int b = opt.Int('b', "<var_name> - usage of -b", 2);
+ bool c = opt.Has('c', "usage of -c");
+ int d = opt.Int('d', "<var_name> - usage of -d", 13);
+ const char* e = opt.Arg('e', "<unused> - only default is really used", edef);
+ const TVector<const char*>& x = opt.MArg('x', "<var_name> - usage of -x");
+
+ UNIT_ASSERT(!opt.AutoUsage("<L> <M>"));
+ UNIT_ASSERT_VALUES_EQUAL("aaaa", a);
+ UNIT_ASSERT_VALUES_EQUAL(2, b);
+ UNIT_ASSERT(c);
+ UNIT_ASSERT_VALUES_EQUAL(8, d);
+ UNIT_ASSERT_VALUES_EQUAL((void*)edef, e);
+
+ UNIT_ASSERT_VALUES_EQUAL(2u, opt.Pos.size());
+ UNIT_ASSERT_STRINGS_EQUAL("zz", opt.Pos.at(0));
+ UNIT_ASSERT_VALUES_EQUAL((void*)argv[2], opt.Pos.at(0));
+ UNIT_ASSERT_STRINGS_EQUAL("ww", opt.Pos.at(1));
+ UNIT_ASSERT_STRINGS_EQUAL("1", x.at(0));
+ UNIT_ASSERT_STRINGS_EQUAL("2", x.at(1));
+ }
+
+ Y_UNIT_TEST(TestErrors1) {
+ int argc = 4;
+ char* argv[] = {
+ (char*)"cmd",
+ (char*)"zz",
+ (char*)"-c",
+ (char*)"-e",
+ };
+
+ Opt2 opt(argc, argv, "ce:", 2);
+
+ const char* edef = "edef";
+ bool c = opt.Has('c', "usage of -c");
+ const char* e = opt.Arg('e', "<unused> - only default is really used", edef);
+ UNIT_ASSERT(c);
+ UNIT_ASSERT_VALUES_EQUAL((void*)edef, e);
+ }
+}
diff --git a/library/cpp/getopt/ut/opt_ut.cpp b/library/cpp/getopt/ut/opt_ut.cpp
new file mode 100644
index 0000000000..441aa493a0
--- /dev/null
+++ b/library/cpp/getopt/ut/opt_ut.cpp
@@ -0,0 +1,52 @@
+#include <library/cpp/getopt/opt.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+#include <util/string/vector.h>
+
+Y_UNIT_TEST_SUITE(OptTest) {
+ Y_UNIT_TEST(TestSimple) {
+ int argc = 3;
+ char* argv[] = {
+ (char*)"cmd", (char*)"-x"};
+ Opt opt(argc, argv, "");
+ opt.Err = false; // be quiet
+ UNIT_ASSERT_VALUES_EQUAL('?', opt.Get());
+ UNIT_ASSERT_VALUES_EQUAL(EOF, opt.Get());
+ UNIT_ASSERT_VALUES_EQUAL(EOF, opt.Get());
+ UNIT_ASSERT_VALUES_EQUAL(EOF, opt.Get());
+ }
+
+ Y_UNIT_TEST(TestFreeArguments) {
+ Opt::Ion options[] = {
+ {"some-option", Opt::WithArg, nullptr, 123},
+ {nullptr, Opt::WithoutArg, nullptr, 0}};
+ const char* argv[] = {"cmd", "ARG1", "-some-option", "ARG2", "ARG3", nullptr};
+ int argc = 5;
+ Opt opts(argc, argv, "", options);
+
+ UNIT_ASSERT_VALUES_EQUAL(JoinStrings(opts.GetFreeArgs(), ", "), "ARG1, ARG3");
+ }
+
+ Y_UNIT_TEST(TestLongOption) {
+ const int SOME_OPTION_ID = 12345678;
+ Opt::Ion options[] = {
+ {"some-option", Opt::WithArg, nullptr, SOME_OPTION_ID},
+ {nullptr, Opt::WithoutArg, nullptr, 0}};
+ for (int doubleDash = 0; doubleDash <= 1; ++doubleDash) {
+ const char* argv[] = {"cmd", "ARG1", (doubleDash ? "--some-option" : "-some-option"), "ARG2", "ARG3", nullptr};
+ int argc = 5;
+ Opt opts(argc, argv, "", options);
+
+ TString optionValue = "";
+ int optlet = 0;
+ while ((optlet = opts.Get()) != EOF) {
+ if (optlet == SOME_OPTION_ID) {
+ optionValue = opts.GetArg();
+ } else {
+ UNIT_FAIL("don't expected any options, except -some-option");
+ }
+ }
+ UNIT_ASSERT_VALUES_EQUAL(optionValue, "ARG2");
+ }
+ }
+}
diff --git a/library/cpp/getopt/ut/posix_getopt_ut.cpp b/library/cpp/getopt/ut/posix_getopt_ut.cpp
new file mode 100644
index 0000000000..b6d374bf28
--- /dev/null
+++ b/library/cpp/getopt/ut/posix_getopt_ut.cpp
@@ -0,0 +1,119 @@
+#include <library/cpp/getopt/posix_getopt.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+using namespace NLastGetopt;
+
+Y_UNIT_TEST_SUITE(TPosixGetoptTest) {
+ Y_UNIT_TEST(TestSimple) {
+ int argc = 6;
+ const char* argv0[] = {"program", "-b", "-f1", "-f", "2", "zzzz"};
+ char** const argv = (char**)argv0;
+
+ NLastGetopt::optreset = 1;
+ UNIT_ASSERT_VALUES_EQUAL('b', NLastGetopt::getopt(argc, argv, "bf:"));
+ UNIT_ASSERT_VALUES_EQUAL('f', NLastGetopt::getopt(argc, argv, "bf:"));
+ UNIT_ASSERT_VALUES_EQUAL(NLastGetopt::optarg, TString("1"));
+ UNIT_ASSERT_VALUES_EQUAL('f', NLastGetopt::getopt(argc, argv, "bf:"));
+ UNIT_ASSERT_VALUES_EQUAL(NLastGetopt::optarg, TString("2"));
+ UNIT_ASSERT_VALUES_EQUAL(-1, NLastGetopt::getopt(argc, argv, "bf:"));
+
+ UNIT_ASSERT_VALUES_EQUAL(5, NLastGetopt::optind);
+ }
+
+ Y_UNIT_TEST(TestLong) {
+ int daggerset = 0;
+ /* options descriptor */
+ const NLastGetopt::option longopts[] = {
+ {"buffy", no_argument, nullptr, 'b'},
+ {"fluoride", required_argument, nullptr, 'f'},
+ {"daggerset", no_argument, &daggerset, 1},
+ {nullptr, 0, nullptr, 0}};
+
+ int argc = 7;
+ const char* argv0[] = {"program", "-b", "--buffy", "-f1", "--fluoride=2", "--daggerset", "zzzz"};
+ char** const argv = (char**)argv0;
+
+ int longIndex;
+
+ NLastGetopt::optreset = 1;
+ UNIT_ASSERT_VALUES_EQUAL('b', NLastGetopt::getopt_long(argc, argv, "bf:", longopts, &longIndex));
+ UNIT_ASSERT_VALUES_EQUAL(0, longIndex);
+ UNIT_ASSERT_VALUES_EQUAL('b', NLastGetopt::getopt_long(argc, argv, "bf:", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL('f', NLastGetopt::getopt_long(argc, argv, "bf:", longopts, &longIndex));
+ UNIT_ASSERT_VALUES_EQUAL(1, longIndex);
+ UNIT_ASSERT_VALUES_EQUAL('f', NLastGetopt::getopt_long(argc, argv, "bf:", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL(0, NLastGetopt::getopt_long(argc, argv, "bf:", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL(-1, NLastGetopt::getopt_long(argc, argv, "bf:", longopts, nullptr));
+
+ UNIT_ASSERT_VALUES_EQUAL(6, NLastGetopt::optind);
+ }
+
+ Y_UNIT_TEST(TestLongPermutation) {
+ int daggerset = 0;
+ /* options descriptor */
+ const NLastGetopt::option longopts[] = {
+ {"buffy", no_argument, nullptr, 'b'},
+ {"fluoride", required_argument, nullptr, 'f'},
+ {"daggerset", no_argument, &daggerset, 1},
+ {nullptr, 0, nullptr, 0}};
+
+ int argc = 7;
+ const char* argv0[] = {"program", "aa", "-b", "bb", "cc", "--buffy", "dd"};
+ char** const argv = (char**)argv0;
+
+ NLastGetopt::optreset = 1;
+ UNIT_ASSERT_VALUES_EQUAL('b', NLastGetopt::getopt_long(argc, argv, "bf:", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL('b', NLastGetopt::getopt_long(argc, argv, "bf:", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL(-1, NLastGetopt::getopt_long(argc, argv, "bf:", longopts, nullptr));
+
+ UNIT_ASSERT_VALUES_EQUAL(3, NLastGetopt::optind);
+ }
+
+ Y_UNIT_TEST(TestNoOptionsOptionsWithDoubleDash) {
+ const NLastGetopt::option longopts[] = {
+ {"buffy", no_argument, nullptr, 'b'},
+ {"fluoride", no_argument, nullptr, 'f'},
+ {nullptr, 0, nullptr, 0}};
+
+ int argc = 2;
+ const char* argv0[] = {"program", "--bf"};
+ char** const argv = (char**)argv0;
+
+ NLastGetopt::optreset = 1;
+ UNIT_ASSERT_VALUES_EQUAL('?', NLastGetopt::getopt_long(argc, argv, "bf", longopts, nullptr));
+ }
+
+ Y_UNIT_TEST(TestLongOnly) {
+ const NLastGetopt::option longopts[] = {
+ {"foo", no_argument, nullptr, 'F'},
+ {"fluoride", no_argument, nullptr, 'f'},
+ {"ogogo", no_argument, nullptr, 'o'},
+ {nullptr, 0, nullptr, 0}};
+
+ int argc = 4;
+ const char* argv0[] = {"program", "--foo", "-foo", "-fo"};
+ char** const argv = (char**)argv0;
+
+ NLastGetopt::optreset = 1;
+ UNIT_ASSERT_VALUES_EQUAL('F', NLastGetopt::getopt_long_only(argc, argv, "fo", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL('F', NLastGetopt::getopt_long_only(argc, argv, "fo", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL('f', NLastGetopt::getopt_long_only(argc, argv, "fo", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL('o', NLastGetopt::getopt_long_only(argc, argv, "fo", longopts, nullptr));
+ UNIT_ASSERT_VALUES_EQUAL(-1, NLastGetopt::getopt_long_only(argc, argv, "fo", longopts, nullptr));
+ }
+
+ Y_UNIT_TEST(TestLongWithoutOnlySingleDashNowAllowed) {
+ const NLastGetopt::option longopts[] = {
+ {"foo", no_argument, nullptr, 'F'},
+ {"zoo", no_argument, nullptr, 'z'},
+ {nullptr, 0, nullptr, 0}};
+
+ int argc = 2;
+ const char* argv0[] = {"program", "-foo"};
+ char** const argv = (char**)argv0;
+
+ NLastGetopt::optreset = 1;
+ UNIT_ASSERT_VALUES_EQUAL('?', NLastGetopt::getopt_long(argc, argv, "z", longopts, nullptr));
+ }
+}
diff --git a/library/cpp/getopt/ut/wrap.cpp b/library/cpp/getopt/ut/wrap.cpp
new file mode 100644
index 0000000000..3444eea102
--- /dev/null
+++ b/library/cpp/getopt/ut/wrap.cpp
@@ -0,0 +1,96 @@
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <library/cpp/getopt/small/wrap.h>
+
+Y_UNIT_TEST_SUITE(Wrap) {
+ Y_UNIT_TEST(TestWrapping) {
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b c d eeffeff").Quote(),
+ TString("a b c\nd\neeffeff").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b\nc d\neeffeff").Quote(),
+ TString("a b\nc d\neeffeff").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b\n c d\neeffeff").Quote(),
+ TString("a b\n c\nd\neeffeff").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b\nx c d\neeffeff").Quote(),
+ TString("a b\nx\nc d\neeffeff").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(),
+ TString("a b\nx\n c\nd\neeffeff").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(),
+ TString("a b\nx\n c\nd\neeffeff").Quote()
+ );
+ }
+
+ Y_UNIT_TEST(TestWrappingIndent) {
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b c d", "|>").Quote(),
+ TString("a b\n|>c d").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b\n\nc d", "|>").Quote(),
+ TString("a b\n|>\n|>c d").Quote()
+ );
+ }
+
+ Y_UNIT_TEST(TestWrappingAnsi) {
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote(),
+ TString("\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a \033[1;2;3;4;5mb c\033[1;2;3;4;5m \033[1;2;3;4;5md e f").Quote(),
+ TString("a \033[1;2;3;4;5mb c\033[1;2;3;4;5m\n\033[1;2;3;4;5md e f").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(),
+ TString("a b \033[1;2;3;4;5m\nc d").Quote()
+ );
+
+ UNIT_ASSERT_STRINGS_EQUAL(
+ NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(),
+ TString("a b\n\033[1;2;3;4;5m c d").Quote()
+ );
+ }
+
+ Y_UNIT_TEST(TestTextInfo) {
+ size_t lastLineLen;
+ bool hasParagraphs;
+
+ NLastGetopt::Wrap(5, "a b c d e", "", &lastLineLen, &hasParagraphs);
+ UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 3);
+ UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false);
+
+ NLastGetopt::Wrap(5, "a b c\n\nd e f h", "", &lastLineLen, &hasParagraphs);
+ UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 1);
+ UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true);
+
+ NLastGetopt::Wrap(5, "a b c\n\n", "", &lastLineLen, &hasParagraphs);
+ UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 0);
+ UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true);
+
+ NLastGetopt::Wrap(5, "\n \na b c", "", &lastLineLen, &hasParagraphs);
+ UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5);
+ UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true);
+
+ NLastGetopt::Wrap(5, "\nx\na b c", "", &lastLineLen, &hasParagraphs);
+ UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5);
+ UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false);
+ }
+}
diff --git a/library/cpp/getopt/ut/ya.make b/library/cpp/getopt/ut/ya.make
new file mode 100644
index 0000000000..8d00669efb
--- /dev/null
+++ b/library/cpp/getopt/ut/ya.make
@@ -0,0 +1,15 @@
+UNITTEST_FOR(library/cpp/getopt)
+
+OWNER(pg)
+
+SRCS(
+ last_getopt_ut.cpp
+ modchooser_ut.cpp
+ opt2_ut.cpp
+ opt_ut.cpp
+ posix_getopt_ut.cpp
+ wrap.cpp
+ ygetopt_ut.cpp
+)
+
+END()
diff --git a/library/cpp/getopt/ut/ygetopt_ut.cpp b/library/cpp/getopt/ut/ygetopt_ut.cpp
new file mode 100644
index 0000000000..a76f117216
--- /dev/null
+++ b/library/cpp/getopt/ut/ygetopt_ut.cpp
@@ -0,0 +1,45 @@
+#include <library/cpp/getopt/ygetopt.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+class TGetOptTest: public TTestBase {
+ UNIT_TEST_SUITE(TGetOptTest);
+ UNIT_TEST(TestGetOpt);
+ UNIT_TEST_EXCEPTION(TestZeroArgC, yexception);
+ UNIT_TEST_SUITE_END();
+
+public:
+ void TestGetOpt();
+ void TestZeroArgC();
+};
+
+UNIT_TEST_SUITE_REGISTRATION(TGetOptTest);
+
+void TGetOptTest::TestZeroArgC() {
+ TGetOpt opt(0, nullptr, "");
+}
+
+void TGetOptTest::TestGetOpt() {
+ const char* argv[] = {
+ "/usr/bin/bash",
+ "-f",
+ "-p",
+ "qwerty123",
+ "-z",
+ "-q",
+ nullptr};
+
+ TString res;
+ const TString format = "qzp:f";
+ TGetOpt opt(sizeof(argv) / sizeof(*argv) - 1, argv, format);
+
+ for (TGetOpt::TIterator it = opt.Begin(); it != opt.End(); ++it) {
+ res += it->Key();
+
+ if (it->HaveArg()) {
+ res += it->Arg();
+ }
+ }
+
+ UNIT_ASSERT_EQUAL(res, "fpqwerty123zq");
+}