From 9c49863795f9ca066c299ec51006058eb01f3ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Sad=C5=82ocha?= Date: Sat, 27 Jul 2024 01:30:54 +0100 Subject: [PATCH] Make `task news` nag configurable and deterministic (#3567) This patch fixes #3497. --- doc/man/taskrc.5.in | 3 +- src/Context.cpp | 5 +-- src/commands/CmdCustom.cpp | 26 +++++--------- test/CMakeLists.txt | 1 + test/news.test.py | 69 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 20 deletions(-) create mode 100755 test/news.test.py diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index ce7be4c23..96767beea 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -303,6 +303,7 @@ control specific occasions when output is generated. This list may contain: label Column labels on tabular reports new-id Provides feedback on any new task with IDs (and UUIDs for new tasks with ID 0, such as new completed tasks). new-uuid Provides feedback on any new task with UUIDs. Overrides new-id. Useful for automation. + news Reminds to read new release highlights until the user runs "task news". affected Reports 'N tasks affected' and similar edit Used the verbose template for the 'edit' command special Feedback when applying special tags @@ -327,7 +328,7 @@ Here are the shortcut equivalents: .nf verbose=on - verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,filter,override,recur + verbose=blank,header,footnote,label,new-id,news,affected,edit,special,project,sync,filter,override,recur .fi .nf diff --git a/src/Context.cpp b/src/Context.cpp index 108721118..0f66aebba 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -93,8 +93,8 @@ std::string configurationDefaults = "\n" "# Miscellaneous\n" "# verbose= # Comma-separated list. May contain any subset of:\n" - "# affected,blank,context,default,edit,filter,footnote,header,label,new-id,new-uuid,override,project,recur,special,sync\n" - "verbose=affected,blank,context,edit,header,footnote,label,new-id,project,special,sync,override,recur\n" + "# affected,blank,context,default,edit,filter,footnote,header,label,new-id,new-uuid,news,override,project,recur,special,sync\n" + "verbose=affected,blank,context,edit,header,footnote,label,new-id,news,project,special,sync,override,recur\n" "confirmation=1 # Confirmation on delete, big changes\n" "recurrence=1 # Enable recurrence\n" "recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n" @@ -1050,6 +1050,7 @@ bool Context::verbose (const std::string& token) v != "label" && // v != "new-id" && // v != "new-uuid" && // + v != "news" && // v != "override" && // v != "project" && // v != "recur" && // diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index b1e75a3eb..378cdc11e 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -254,23 +253,16 @@ int CmdCustom::execute (std::string& output) // Inform user about the new release highlights if not presented yet Version news_version(Context::getContext ().config.get ("news.version")); Version current_version = Version::Current(); - if (news_version != current_version) + auto should_nag = news_version != current_version && Context::getContext ().verbose ("news"); + if (should_nag) { - std::random_device device; - std::mt19937 random_generator(device()); - std::uniform_int_distribution twentyfive_percent(1, 4); - - // 25% chance to display the message. - if (twentyfive_percent(random_generator) == 4) - { - std::ostringstream notice; - notice << "Recently upgraded to " << current_version << ". " - "Please run 'task news' to read highlights about the new release."; - if (Context::getContext ().verbose ("footnote")) - Context::getContext ().footnote (notice.str()); - else if (Context::getContext ().verbose ("header")) - Context::getContext ().header (notice.str()); - } + std::ostringstream notice; + notice << "Recently upgraded to " << current_version << ". " + "Please run 'task news' to read highlights about the new release."; + if (Context::getContext ().verbose ("footnote")) + Context::getContext ().footnote (notice.str()); + else if (Context::getContext ().verbose ("header")) + Context::getContext ().header (notice.str()); } std::string location = (Context::getContext ().data_dir); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d450df5c8..57c81bf5f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -138,6 +138,7 @@ set (pythonTests math.test.py modify.test.py nag.test.py + news.test.py obfuscate.test.py oldest.test.py operators.test.py diff --git a/test/news.test.py b/test/news.test.py new file mode 100755 index 000000000..f4ff93c19 --- /dev/null +++ b/test/news.test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +############################################################################### +# +# Copyright 2024, Adrian Sadłocha. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# https://www.opensource.org/licenses/mit-license.php +# +############################################################################### + +import os +import sys +import unittest + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase + + +class TestNewsNag(TestCase): + def setUp(self): + self.t = Task() + self.t("add Sample") + + def test_news_nag_gets_displayed_with_default_verbosity_levels(self): + """Default verbosity""" + + _, _, err = self.t("") + self.assertIn("Please run 'task news'", err) + + def test_news_nag_gets_displayed_when_explicitly_toggled_on(self): + """Explicitly toggled on""" + + # Add `footnote` so there is a sink for the nag message. + _, _, err = self.t("rc.verbose:news,footnote") + self.assertIn("Please run 'task news'", err) + + def test_news_nag_does_not_get_displayed_when_explicitly_toggled_off(self): + """Explicitly toggled off""" + + # Add `footnote` so there is a sink for the nag message. + _, _, err = self.t("rc.verbose:footnote") + self.assertNotIn("Please run 'task news'", err) + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python