- Modified argument parsing to prevent assertions and seg faults.
- Added support for month names, with autocomplete.
- Added autocomplete for "due".
- Added unit tests to exercise all combinations, and errors.
This commit is contained in:
Paul Beckingham 2010-08-09 01:20:05 -04:00
parent d1abda2561
commit ae56165c80
2 changed files with 209 additions and 41 deletions

View file

@ -2030,54 +2030,103 @@ int handleReportCalendar (std::string &outs)
int mTo = mFrom; int mTo = mFrom;
int yTo = yFrom; int yTo = yFrom;
// Determine what to do // Defaults.
int numberOfArgs = context.args.size(); monthsToDisplay = monthsPerLine;
mFrom = today.month ();
yFrom = today.year ();
if (numberOfArgs == 1) { // Set up a vector of commands (1), for autoComplete.
// task cal std::vector <std::string> commandNames;
monthsToDisplay = monthsPerLine; commandNames.push_back ("calendar");
mFrom = today.month();
yFrom = today.year(); // Set up a vector of keywords, for autoComplete.
} std::vector <std::string> keywordNames;
else if (numberOfArgs == 2) { keywordNames.push_back ("due");
if (context.args[1] == "y") {
// task cal y // Set up a vector of months, for autoComplete.
monthsToDisplay = 12; std::vector <std::string> monthNames;
mFrom = today.month(); monthNames.push_back ("january");
yFrom = today.year(); monthNames.push_back ("february");
} monthNames.push_back ("march");
else if (context.args[1] == "due") { monthNames.push_back ("april");
// task cal due monthNames.push_back ("may");
monthsToDisplay = monthsPerLine; monthNames.push_back ("june");
monthNames.push_back ("july");
monthNames.push_back ("august");
monthNames.push_back ("september");
monthNames.push_back ("october");
monthNames.push_back ("november");
monthNames.push_back ("december");
// For autoComplete results.
std::vector <std::string> matches;
// Look at all args, regardless of sequence.
int argMonth = 0;
int argYear = 0;
bool argWholeYear = false;
foreach (arg, context.args)
{
// Some version of "calendar".
if (autoComplete (lowerCase (*arg), commandNames, matches) == 1)
continue;
// "due".
else if (autoComplete (lowerCase (*arg), keywordNames, matches) == 1)
getpendingdate = true; getpendingdate = true;
// "y".
else if (lowerCase (*arg) == "y")
argWholeYear = true;
// YYYY.
else if (digitsOnly (*arg) && arg->length () == 4)
argYear = atoi (arg->c_str ());
// MM.
else if (digitsOnly (*arg) && arg->length () <= 2)
{
argMonth = atoi (arg->c_str ());
if (argMonth < 1 || argMonth > 12)
throw std::string ("Argument '") + *arg + "' is not a valid month.";
} }
else {
// task cal 2010 // "January" etc.
else if (autoComplete (lowerCase (*arg), monthNames, matches) == 1)
{
argMonth = Date::monthOfYear (matches[0]);
if (argMonth == -1)
throw std::string ("Argument '") + *arg + "' is not a valid month.";
}
else
throw std::string ("Could not recognize argument '") + *arg + "'.";
}
// Supported combinations:
//
// Command line monthsToDisplay mFrom yFrom getpendingdate
// ------------ --------------- ----- ----- --------------
// cal monthsPerLine today today false
// cal y 12 today today false
// cal due monthsPerLine today today true
// cal YYYY 12 1 arg false
// cal due y 12 today today true
// cal MM YYYY monthsPerLine arg arg false
// cal MM YYYY y 12 arg arg false
if (argWholeYear || (argYear && !argMonth && !argWholeYear))
monthsToDisplay = 12; monthsToDisplay = 12;
if (!argMonth && argYear)
mFrom = 1; mFrom = 1;
yFrom = atoi (context.args[1].c_str ()); else if (argMonth && argYear)
} mFrom = argMonth;
}
else if (numberOfArgs == 3) {
if (context.args[2] == "y") {
// task cal due y
monthsToDisplay = 12;
getpendingdate = true;
}
else {
// task cal 8 2010
monthsToDisplay = monthsPerLine;
mFrom = atoi (context.args[1].c_str ());
yFrom = atoi (context.args[2].c_str ());
}
}
else if (numberOfArgs == 4) {
// task cal 8 2010 y
monthsToDisplay = 12;
mFrom = atoi (context.args[1].c_str ());
yFrom = atoi (context.args[2].c_str ());
}
if (argYear)
yFrom = argYear;
// Now begin the data subset and rendering.
int countDueDates = 0; int countDueDates = 0;
if (getpendingdate == true) { if (getpendingdate == true) {
// Find the oldest pending due date. // Find the oldest pending due date.

119
src/tests/bug.cal.t Executable file
View file

@ -0,0 +1,119 @@
#! /usr/bin/perl
################################################################################
## task - a command line task list manager.
##
## Copyright 2006 - 2010, Paul Beckingham.
## All rights reserved.
##
## This program is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the
##
## Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor,
## Boston, MA
## 02110-1301
## USA
##
################################################################################
use strict;
use warnings;
use Test::More tests => 32;
# Create the rc file.
if (open my $fh, '>', 'cal.rc')
{
print $fh "data.location=.";
close $fh;
ok (-r 'cal.rc', 'Created cal.rc');
}
# Bug: The 'cal' command can fail when provided with challenging arguments.
# Should not fail (because they are correct):
my $output = qx{../task rc:cal.rc cal 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal');
# y due 2010 donkey 8
$output = qx{../task rc:cal.rc cal y 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y');
$output = qx{../task rc:cal.rc cal 8 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 8');
$output = qx{../task rc:cal.rc cal due 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal due');
$output = qx{../task rc:cal.rc cal 2010 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 2010');
$output = qx{../task rc:cal.rc cal donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal donkey');
# y due 2010 donkey 8
$output = qx{../task rc:cal.rc cal y due 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y due');
$output = qx{../task rc:cal.rc cal y 8 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y 8');
$output = qx{../task rc:cal.rc cal y 2010 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y 2010');
$output = qx{../task rc:cal.rc cal y donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y donkey');
$output = qx{../task rc:cal.rc cal 8 due 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 8 due');
$output = qx{../task rc:cal.rc cal 8 2010 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 8 2010');
$output = qx{../task rc:cal.rc cal 8 donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 8 donkey');
$output = qx{../task rc:cal.rc cal due 2010 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal due 2010');
$output = qx{../task rc:cal.rc cal due donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal due donkey');
$output = qx{../task rc:cal.rc cal 2010 donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 2010 donkey');
# y 8 due 2010 donkey
$output = qx{../task rc:cal.rc cal y 8 due 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y 8 due');
$output = qx{../task rc:cal.rc cal y 8 2010 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y 8 2010');
$output = qx{../task rc:cal.rc cal y 8 donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y 8 donkey');
$output = qx{../task rc:cal.rc cal y due 2010 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y due 2010');
$output = qx{../task rc:cal.rc cal y due donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y due donkey');
$output = qx{../task rc:cal.rc cal y 2010 donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal y 2010 donkey');
$output = qx{../task rc:cal.rc cal 8 due 2010 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 8 due 2010');
$output = qx{../task rc:cal.rc cal 8 due donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 8 due donkey');
$output = qx{../task rc:cal.rc cal 8 2010 donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal 8 2010 donkey');
$output = qx{../task rc:cal.rc cal due 2010 8 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal due 2010 8');
$output = qx{../task rc:cal.rc cal due 2010 donkey 2>&1};
unlike ($output, qr/(?:Assertion failed|Could note recognize|not a valid)/, 'cal due 2010 donkey');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'completed.data';
ok (!-r 'completed.data', 'Removed completed.data');
unlink 'undo.data';
ok (!-r 'undo.data', 'Removed undo.data');
unlink 'cal.rc';
ok (!-r 'cal.rc', 'Removed cal.rc');
exit 0;