// // action_test.cpp // program_args-test // // Created by Sam Jaffe on 2/13/22. // #include "program_args/arguments.h" #include "xcode_gtest_helper.h" using testing::NotNull; namespace program { struct ArgumentTestHelper { template static int main(Impl const & args) { return args.invoke(); } }; } template static T parse(char const * const (&argv)[N]) { return T(N, argv); } program::detail::Any g_action; template int typed_main(Arg const &arg, Args const &...args) { auto const &last = std::get(std::forward_as_tuple(arg, args...)); g_action = program::detail::Any(&last); return 0; } struct Checkout : program::Arguments { using Arguments::Arguments; std::string commitish = argument(0, "commit-ish"); }; struct Commit : program::Arguments { using Arguments::Arguments; std::string message = option("message", 'm'); }; class Push : public program::Arguments { public: using Arguments::Arguments; std::string remote = argument(0, "repository") = PROGRAM_DEFER(_remote()); private: std::string _remote() const; }; struct SetUrl : program::Arguments { using Arguments::Arguments; bool push = flag("push") = false; std::string name = argument(0, "name"); std::string new_url = argument(1, "newurl"); std::string old_url = argument(2, "oldurl") = ""; }; struct Show : program::Arguments { using Arguments::Arguments; bool use_cached = flag('n') = false; }; struct Remote : public program::Arguments { PROGRAM_ARGS_INVOKE_WITH_DEFAULT(show, show, set_url); bool verbose = flag('v') = false; SetUrl set_url = action("set-url"); Show show = action("show"); }; struct Bad1 : program::Arguments { using Arguments::Arguments; Commit commit = action("commit"); std::string arg0 = argument(0, "arg0"); }; struct Bad2 : program::Arguments { using Arguments::Arguments; std::string arg0 = argument(0, "arg0"); Commit commit = action("commit"); }; struct Git : program::Arguments { PROGRAM_ARGS_INVOKE(commit, checkout, push, remote); std::string pwd = option('C'); bool verbose = flag("verbose", 'v'); Commit commit = action("commit"); Checkout checkout = action("checkout"); Push push = action("push"); Remote remote = action("remote"); }; struct GitPlug : program::Arguments { PROGRAM_ARGS_INVOKE_WITH_DEFAULT(*this, commit, checkout); Commit commit = action("commit"); Checkout checkout = action("checkout"); }; std::string Push::_remote() const { if (Git const * git = Arguments::parent()) { return git->pwd; } return ""; } TEST(ActionTest, CannotMixActionAndArgument) { EXPECT_THROW(Bad1(), program::ArgumentMixingError); EXPECT_THROW(Bad2(), program::ArgumentMixingError); } TEST(ActionTest, CanRunWithMultipleActions) { EXPECT_NO_THROW(Git()); } TEST(ActionTest, ActionIsRouted) { Git git = parse({"", "-v", "commit", "-m", "this is a message"}); EXPECT_THAT(git.commit.message, "this is a message"); } TEST(ActionTest, CanDiveIntoTypedMain) { Git git = parse({"", "-v", "commit", "-m", "this is a message"}); program::ArgumentTestHelper::main(git); EXPECT_THAT(g_action.get(), NotNull()); } TEST(ActionTest, CanFetchParentInfo) { Git git = parse({"", "-C", "./submodules/X", "push"}); EXPECT_THAT(git.push.remote, "./submodules/X"); } TEST(ActionTest, ReturnsFailureOnNoAction) { Git git = parse({""}); EXPECT_THAT(program::ArgumentTestHelper::main(git), 1); } TEST(ActionTest, CanRecursivelyPerformActions) { Git git = parse({"", "remote", "-v", "show", "-n"}); EXPECT_TRUE(git.remote.verbose); EXPECT_TRUE(git.remote.show.use_cached); program::ArgumentTestHelper::main(git); EXPECT_THAT(g_action.get(), NotNull()); } TEST(ActionTest, CanStoreDefaultAction) { Git git = parse({"", "remote"}); program::ArgumentTestHelper::main(git); EXPECT_THAT(g_action.get(), NotNull()); } TEST(ActionTest, DefaultActionCanBeSelf) { GitPlug git = parse({""}); program::ArgumentTestHelper::main(git); EXPECT_THAT(g_action.get(), NotNull()); }