From d0a2aa7908aee8d55babab21a22684879f97f97d Mon Sep 17 00:00:00 2001 From: dryleev Date: Thu, 24 Jun 2021 16:39:43 +0300 Subject: [PATCH] fix: remove duplicated call to subcommand's final_callback (#584) --- include/CLI/App.hpp | 14 ++++++++------ tests/SubcommandTest.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 816ee21..9bd7599 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -113,6 +113,7 @@ class App { /// This is a function that runs when parsing has finished. std::function parse_complete_callback_{}; + /// This is a function that runs when all processing has completed std::function final_callback_{}; @@ -1934,8 +1935,9 @@ class App { app->_configure(); } } + /// Internal function to run (App) callback, bottom up - void run_callback(bool final_mode = false) { + void run_callback(bool final_mode = false, bool suppress_final_callback = false) { pre_callback(); // in the main app if immediate_callback_ is set it runs the main callback before the used subcommands if(!final_mode && parse_complete_callback_) { @@ -1943,18 +1945,18 @@ class App { } // run the callbacks for the received subcommands for(App *subc : get_subcommands()) { - subc->run_callback(true); + subc->run_callback(true, suppress_final_callback); } // now run callbacks for option_groups for(auto &subc : subcommands_) { if(subc->name_.empty() && subc->count_all() > 0) { - subc->run_callback(true); + subc->run_callback(true, suppress_final_callback); } } // finally run the main callback - if(final_callback_ && (parsed_ > 0)) { - if(!name_.empty() || count_all() > 0) { + if(final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) { + if(!name_.empty() || count_all() > 0 || parent_ == nullptr) { final_callback_(); } } @@ -2337,7 +2339,7 @@ class App { _process_callbacks(); _process_help_flags(); _process_requirements(); - run_callback(); + run_callback(false, true); } } diff --git a/tests/SubcommandTest.cpp b/tests/SubcommandTest.cpp index 2016615..88a147d 100644 --- a/tests/SubcommandTest.cpp +++ b/tests/SubcommandTest.cpp @@ -1874,3 +1874,63 @@ TEST_CASE_METHOD(ManySubcommands, "defaultEnabledSubcommand", "[subcom]") { CHECK(sub2->get_enabled_by_default()); CHECK(!sub2->get_disabled()); } + +// #572 +TEST_CASE_METHOD(TApp, "MultiFinalCallbackCounts", "[subcom]") { + + int app_compl = 0; + int sub_compl = 0; + int subsub_compl = 0; + int app_final = 0; + int sub_final = 0; + int subsub_final = 0; + + app.parse_complete_callback([&app_compl]() { app_compl++; }); + app.final_callback([&app_final]() { app_final++; }); + + auto *sub = app.add_subcommand("sub"); + + sub->parse_complete_callback([&sub_compl]() { sub_compl++; }); + sub->final_callback([&sub_final]() { sub_final++; }); + + auto *subsub = sub->add_subcommand("subsub"); + + subsub->parse_complete_callback([&subsub_compl]() { subsub_compl++; }); + subsub->final_callback([&subsub_final]() { subsub_final++; }); + + SECTION("No specified subcommands") { + args = {}; + run(); + + CHECK(app_compl == 1); + CHECK(app_final == 1); + CHECK(sub_compl == 0); + CHECK(sub_final == 0); + CHECK(subsub_compl == 0); + CHECK(subsub_final == 0); + } + + SECTION("One layer of subcommands") { + args = {"sub"}; + run(); + + CHECK(app_compl == 1); + CHECK(app_final == 1); + CHECK(sub_compl == 1); + CHECK(sub_final == 1); + CHECK(subsub_compl == 0); + CHECK(subsub_final == 0); + } + + SECTION("Fully specified subcommands") { + args = {"sub", "subsub"}; + run(); + + CHECK(app_compl == 1); + CHECK(app_final == 1); + CHECK(sub_compl == 1); + CHECK(sub_final == 1); + CHECK(subsub_compl == 1); + CHECK(subsub_final == 1); + } +} -- libgit2 0.21.4