Bug #438 - Reports sorting by end_time, start_time, and entry_time are ordered incorrectly

- Fixed bug #438, correcting the sorting of the entry_time, start_time
  and end_time columns (thanks to Michelle Crane).
- Reordered ChangeLog so that bugs, features are in sequence.  Don't know
  why I did this.  Some inner compulsion.
- Deprecated silly start_time, end_time and entry_time columns, which are
  now (and were) superseded by start, end and entry columns with time formats.
- Config.cpp now detects use of these deprecated fields and complains to the
  show command.
- Date.cpp now uses the variable 'input' instead of 'mdy', which was confusing
  and implied that it contained a date without a time.
- Obsoleted and removed Date::toStringWithTime, which ignored requested formats.
- When checking for an epoch, Date::isEpoch just looked for strings of more
  than 8 digits.  The additional restriction of less than or equal to 10 digits
  was added.  This was breaking unit tests using the dateformat YMDHNS, which is
  reasonable.
- Removed the obsolete field format hooks format-entry_time, format-start_time
  and format-end_time
- Removed the obsolete field format hook unit tests hook.format-entry_time.t,
  hook.format-start_time.t and hook.format-end_time.t.
- Removed use of deprecated field in hook.format-countdown_compact.t.
- Added missing shortcut comparisons in Table::sort that was causing an
  unnecessary full parse of dates even if they were identical as strings.
- Coded entry_time as a synonym for entry.  Ditto for start_time and end_time.
- Marked the new synonyms as deprecated.
- Added bug.438.t unit test.
- Added deprecated fields to the NEWS file.
This commit is contained in:
Paul Beckingham 2010-07-20 16:37:47 -07:00
parent a57326a026
commit 1a34a29b7a
16 changed files with 232 additions and 444 deletions

View file

@ -486,7 +486,6 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data)
contents << "# [Created by "
<< PACKAGE_STRING
<< " "
// << now.toStringWithTime ()
<< now.toString ("m/d/Y H:N:S")
<< "]\n"
<< defaults.substr (0, loc + 14)
@ -641,12 +640,16 @@ std::string Config::checkForDeprecatedColumns ()
if (i->first.find ("report") == 0)
{
std::string value = get (i->first);
if (value.find ("entry_time") != std::string::npos)
if (value.find ("entry_time") != std::string::npos ||
value.find ("start_time") != std::string::npos ||
value.find ("end_time") != std::string::npos)
deprecated.push_back (i->first);
}
}
std::stringstream out;
out << std::endl;
if (deprecated.size ())
{
out << "Your .taskrc file contains reports with deprecated columns. "

View file

@ -60,6 +60,7 @@ public:
void all (std::vector <std::string>&);
std::string checkForDeprecatedColor ();
std::string checkForDeprecatedColumns ();
public:
File original_file;

View file

@ -82,7 +82,7 @@ Date::Date (const int m, const int d, const int y,
}
////////////////////////////////////////////////////////////////////////////////
Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
Date::Date (const std::string& input, const std::string& format /* = "m/d/Y" */)
{
int month = 0;
int day = 0;
@ -92,14 +92,14 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
int second = 0;
// Perhaps it is an epoch date, in string form?
if (isEpoch (mdy))
if (isEpoch (input))
return;
// Before parsing according to "format", perhaps this is a relative date?
if (isRelativeDate (mdy))
if (isRelativeDate (input))
return;
unsigned int i = 0; // Index into mdy.
unsigned int i = 0; // Index into input.
for (unsigned int f = 0; f < format.length (); ++f)
{
@ -107,90 +107,90 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
{
// Single or double digit.
case 'm':
if (i >= mdy.length () ||
! isdigit (mdy[i]))
if (i >= input.length () ||
! isdigit (input[i]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (m).";
throw std::string ("\"") + input + "\" is not a valid date (m).";
}
if (i + 1 < mdy.length () &&
(mdy[i + 0] == '0' || mdy[i + 0] == '1') &&
isdigit (mdy[i + 1]))
if (i + 1 < input.length () &&
(input[i + 0] == '0' || input[i + 0] == '1') &&
isdigit (input[i + 1]))
{
month = atoi (mdy.substr (i, 2).c_str ());
month = atoi (input.substr (i, 2).c_str ());
i += 2;
}
else
{
month = atoi (mdy.substr (i, 1).c_str ());
month = atoi (input.substr (i, 1).c_str ());
++i;
}
break;
case 'd':
if (i >= mdy.length () ||
! isdigit (mdy[i]))
if (i >= input.length () ||
! isdigit (input[i]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (d).";
throw std::string ("\"") + input + "\" is not a valid date (d).";
}
if (i + 1 < mdy.length () &&
(mdy[i + 0] == '0' || mdy[i + 0] == '1' || mdy[i + 0] == '2' || mdy[i + 0] == '3') &&
isdigit (mdy[i + 1]))
if (i + 1 < input.length () &&
(input[i + 0] == '0' || input[i + 0] == '1' || input[i + 0] == '2' || input[i + 0] == '3') &&
isdigit (input[i + 1]))
{
day = atoi (mdy.substr (i, 2).c_str ());
day = atoi (input.substr (i, 2).c_str ());
i += 2;
}
else
{
day = atoi (mdy.substr (i, 1).c_str ());
day = atoi (input.substr (i, 1).c_str ());
++i;
}
break;
// Double digit.
case 'y':
if (i + 1 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]))
if (i + 1 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (y).";
throw std::string ("\"") + input + "\" is not a valid date (y).";
}
year = atoi (mdy.substr (i, 2).c_str ()) + 2000;
year = atoi (input.substr (i, 2).c_str ()) + 2000;
i += 2;
break;
case 'M':
if (i + 1 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]))
if (i + 1 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (M).";
throw std::string ("\"") + input + "\" is not a valid date (M).";
}
month = atoi (mdy.substr (i, 2).c_str ());
month = atoi (input.substr (i, 2).c_str ());
i += 2;
break;
case 'D':
if (i + 1 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]))
if (i + 1 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (D).";
throw std::string ("\"") + input + "\" is not a valid date (D).";
}
day = atoi (mdy.substr (i, 2).c_str ());
day = atoi (input.substr (i, 2).c_str ());
i += 2;
break;
case 'V':
if (i + 1 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]))
if (i + 1 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (V).";
throw std::string ("\"") + input + "\" is not a valid date (V).";
}
i += 2;
@ -198,134 +198,134 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
// Quadruple digit.
case 'Y':
if (i + 3 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]) ||
! isdigit (mdy[i + 2]) ||
! isdigit (mdy[i + 3]))
if (i + 3 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]) ||
! isdigit (input[i + 2]) ||
! isdigit (input[i + 3]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (Y).";
throw std::string ("\"") + input + "\" is not a valid date (Y).";
}
year = atoi (mdy.substr (i, 4).c_str ());
year = atoi (input.substr (i, 4).c_str ());
i += 4;
break;
// Short names with 3 characters
case 'a':
if (i + 2 >= mdy.length () ||
isdigit (mdy[i + 0]) ||
isdigit (mdy[i + 1]) ||
isdigit (mdy[i + 2]))
if (i + 2 >= input.length () ||
isdigit (input[i + 0]) ||
isdigit (input[i + 1]) ||
isdigit (input[i + 2]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (a).";
throw std::string ("\"") + input + "\" is not a valid date (a).";
}
i += 3;
break;
case 'b':
if (i + 2 >= mdy.length () ||
isdigit (mdy[i + 0]) ||
isdigit (mdy[i + 1]) ||
isdigit (mdy[i + 2]))
if (i + 2 >= input.length () ||
isdigit (input[i + 0]) ||
isdigit (input[i + 1]) ||
isdigit (input[i + 2]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (b).";
throw std::string ("\"") + input + "\" is not a valid date (b).";
}
month = Date::monthOfYear (mdy.substr (i, 3).c_str());
month = Date::monthOfYear (input.substr (i, 3).c_str());
i += 3;
break;
// Long names
case 'A':
if (i + 2 >= mdy.length () ||
isdigit (mdy[i + 0]) ||
isdigit (mdy[i + 1]) ||
isdigit (mdy[i + 2]))
if (i + 2 >= input.length () ||
isdigit (input[i + 0]) ||
isdigit (input[i + 1]) ||
isdigit (input[i + 2]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (A).";
throw std::string ("\"") + input + "\" is not a valid date (A).";
}
i += Date::dayName( Date::dayOfWeek (mdy.substr (i, 3).c_str()) ).size();
i += Date::dayName( Date::dayOfWeek (input.substr (i, 3).c_str()) ).size();
break;
case 'B':
if (i + 2 >= mdy.length () ||
isdigit (mdy[i + 0]) ||
isdigit (mdy[i + 1]) ||
isdigit (mdy[i + 2]))
if (i + 2 >= input.length () ||
isdigit (input[i + 0]) ||
isdigit (input[i + 1]) ||
isdigit (input[i + 2]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (B).";
throw std::string ("\"") + input + "\" is not a valid date (B).";
}
month = Date::monthOfYear (mdy.substr (i, 3).c_str());
month = Date::monthOfYear (input.substr (i, 3).c_str());
i += Date::monthName(month).size();
break;
// Single or double digit.
case 'h':
if (i >= mdy.length () ||
! isdigit (mdy[i]))
if (i >= input.length () ||
! isdigit (input[i]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (h).";
throw std::string ("\"") + input + "\" is not a valid date (h).";
}
if (i + 1 < mdy.length () &&
(mdy[i + 0] == '0' || mdy[i + 0] == '1' || mdy[i + 0] == '2') &&
isdigit (mdy[i + 1]))
if (i + 1 < input.length () &&
(input[i + 0] == '0' || input[i + 0] == '1' || input[i + 0] == '2') &&
isdigit (input[i + 1]))
{
hour = atoi (mdy.substr (i, 2).c_str ());
hour = atoi (input.substr (i, 2).c_str ());
i += 2;
}
else
{
hour = atoi (mdy.substr (i, 1).c_str ());
hour = atoi (input.substr (i, 1).c_str ());
++i;
}
break;
case 'H':
if (i + 1 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]))
if (i + 1 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (H).";
throw std::string ("\"") + input + "\" is not a valid date (H).";
}
hour = atoi (mdy.substr (i, 2).c_str ());
hour = atoi (input.substr (i, 2).c_str ());
i += 2;
break;
case 'N':
if (i + 1 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]))
if (i + 1 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (N).";
throw std::string ("\"") + input + "\" is not a valid date (N).";
}
minute = atoi (mdy.substr (i, 2).c_str ());
minute = atoi (input.substr (i, 2).c_str ());
i += 2;
break;
case 'S':
if (i + 1 >= mdy.length () ||
! isdigit (mdy[i + 0]) ||
! isdigit (mdy[i + 1]))
if (i + 1 >= input.length () ||
! isdigit (input[i + 0]) ||
! isdigit (input[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date (S).";
throw std::string ("\"") + input + "\" is not a valid date (S).";
}
second = atoi (mdy.substr (i, 2).c_str ());
second = atoi (input.substr (i, 2).c_str ());
i += 2;
break;
default:
if (i >= mdy.length () ||
mdy[i] != format[f])
if (i >= input.length () ||
input[i] != format[f])
{
throw std::string ("\"") + mdy + "\" is not a valid date (DEFAULT).";
throw std::string ("\"") + input + "\" is not a valid date (DEFAULT).";
}
++i;
break;
@ -340,11 +340,11 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
year = default_year->tm_year + 1900;
}
if (i < mdy.length ())
throw std::string ("\"") + mdy + "\" is not a valid date in " + format + " format.";
if (i < input.length ())
throw std::string ("\"") + input + "\" is not a valid date in " + format + " format.";
if (!valid (month, day, year))
throw std::string ("\"") + mdy + "\" is not a valid date (VALID).";
throw std::string ("\"") + input + "\" is not a valid date (VALID).";
// Convert to epoch.
struct tm t = {0};
@ -457,19 +457,6 @@ const std::string Date::toString (const std::string& format /*= "m/d/Y" */) cons
return formatted;
}
////////////////////////////////////////////////////////////////////////////////
const std::string Date::toStringWithTime (const std::string& format /*= "m/d/Y" */) const
{
// Format as above.
std::string formatted = toString (format);
char buffer[12];
sprintf (buffer, " %d:%02d:%02d", hour (), minute (), second ());
formatted += buffer;
return formatted;
}
////////////////////////////////////////////////////////////////////////////////
bool Date::valid (const std::string& input, const std::string& format)
{
@ -846,8 +833,9 @@ time_t Date::operator- (const Date& rhs)
////////////////////////////////////////////////////////////////////////////////
bool Date::isEpoch (const std::string& input)
{
if (digitsOnly (input) &&
input.length () > 8)
if (digitsOnly (input) &&
input.length () > 8 &&
input.length () <= 10 )
{
mT = (time_t) atoi (input.c_str ());
return true;

View file

@ -50,7 +50,6 @@ public:
std::string toISO ();
void toMDY (int&, int&, int&);
const std::string toString (const std::string& format = "m/d/Y") const;
const std::string toStringWithTime (const std::string& format = "m/d/Y") const;
static bool valid (const std::string&, const std::string& format = "m/d/Y");
static bool valid (const int, const int, const int, const int, const int, const int);
static bool valid (const int, const int, const int);

View file

@ -226,11 +226,8 @@ Hooks::Hooks ()
validFieldEvents.push_back ("format-priority");
validFieldEvents.push_back ("format-priority_long");
validFieldEvents.push_back ("format-entry");
validFieldEvents.push_back ("format-entry_time");
validFieldEvents.push_back ("format-start");
validFieldEvents.push_back ("format-start_time");
validFieldEvents.push_back ("format-end");
validFieldEvents.push_back ("format-end_time");
validFieldEvents.push_back ("format-due");
validFieldEvents.push_back ("format-countdown");
validFieldEvents.push_back ("format-countdown_compact");

View file

@ -838,7 +838,7 @@ void TDB::undo ()
row = table.addRow ();
table.addCell (row, 0, "+++ current state "); // Note trailing space.
table.addCell (row, 1, "Change made " + lastChange.toStringWithTime (context.config.get ("dateformat")));
table.addCell (row, 1, "Change made " + lastChange.toString (context.config.get ("dateformat")));
table.setRowColor (row, color_green);
table.addRow ();

View file

@ -698,6 +698,9 @@ void Table::sort (std::vector <int>& order)
case ascendingDate:
{
if (*left == *right)
break;
if ((std::string)*left != "" && (std::string)*right == "")
break;
@ -716,6 +719,9 @@ void Table::sort (std::vector <int>& order)
case descendingDate:
{
if (*left == *right)
break;
if ((std::string)*left != "" && (std::string)*right == "")
break;

View file

@ -776,14 +776,13 @@ int handleShow (std::string &outs)
table.addCell (row, 0, *i);
table.addCell (row, 1, context.config.get (*i));
// Look for unrecognized.
if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"))
if (std::find (unrecognized.begin (), unrecognized.end (), *i) != unrecognized.end ())
table.setRowColor (row, error);
}
}
Color bold ("bold");
out << std::endl
<< table.render ()
<< (table.rowCount () == 0 ? "No matching configuration variables.\n" : "")
@ -806,6 +805,7 @@ int handleShow (std::string &outs)
}
out << context.config.checkForDeprecatedColor ();
out << context.config.checkForDeprecatedColumns ();
// TODO Check for referenced but missing theme files.
// TODO Check for referenced but missing string files.
// TODO Check for referenced but missing tips files.

View file

@ -235,7 +235,7 @@ int handleCustomReport (const std::string& report, std::string &outs)
}
}
else if (*col == "entry")
else if (*col == "entry" || *col == "entry_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added");
table.setColumnWidth (columnCount, Table::minimum);
@ -255,27 +255,7 @@ int handleCustomReport (const std::string& report, std::string &outs)
}
}
else if (*col == "entry_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string entered;
for (unsigned int row = 0; row < tasks.size(); ++row)
{
entered = tasks[row].get ("entry");
if (entered.length ())
{
Date dt (::atoi (entered.c_str ()));
entered = dt.toStringWithTime (context.config.get ("dateformat"));
context.hooks.trigger ("format-entry_time", "entry_time", entered);
table.addCell (row, columnCount, entered);
}
}
}
else if (*col == "start")
else if (*col == "start" || *col == "start_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started");
table.setColumnWidth (columnCount, Table::minimum);
@ -295,27 +275,7 @@ int handleCustomReport (const std::string& report, std::string &outs)
}
}
else if (*col == "start_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{
started = tasks[row].get ("start");
if (started.length ())
{
Date dt (::atoi (started.c_str ()));
started = dt.toStringWithTime (context.config.get ("dateformat"));
context.hooks.trigger ("format-start_time", "start_time", started);
table.addCell (row, columnCount, started);
}
}
}
else if (*col == "end")
else if (*col == "end" || *col == "end_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed");
table.setColumnWidth (columnCount, Table::minimum);
@ -335,28 +295,6 @@ int handleCustomReport (const std::string& report, std::string &outs)
}
}
else if (*col == "end_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string format = context.config.get ("dateformat");
std::string ended;
for (unsigned int row = 0; row < tasks.size(); ++row)
{
ended = tasks[row].get ("end");
if (ended.length ())
{
Date dt (::atoi (ended.c_str ()));
ended = dt.toStringWithTime (format);
context.hooks.trigger ("format-end_time", "end_time", ended);
table.addCell (row, columnCount, ended);
}
}
}
else if (*col == "due")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Due");
@ -645,8 +583,9 @@ int handleCustomReport (const std::string& report, std::string &outs)
Table::ascendingPriority :
Table::descendingPriority));
else if (column == "entry" || column == "start" || column == "wait" ||
column == "until" || column == "end")
else if (column == "entry" || column == "start" || column == "wait" ||
column == "until" || column == "end" || column == "entry_time" ||
column == "start_time" || column == "end_time")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingDate :
@ -753,11 +692,11 @@ void validReportColumns (const std::vector <std::string>& columns)
*it != "priority" &&
*it != "priority_long" &&
*it != "entry" &&
*it != "entry_time" &&
*it != "entry_time" && // TODO Deprecated
*it != "start" &&
*it != "start_time" &&
*it != "start_time" && // TODO Deprecated
*it != "end" &&
*it != "end_time" &&
*it != "end_time" && // TODO Deprecated
*it != "due" &&
*it != "countdown" &&
*it != "countdown_compact" &&

94
src/tests/bug.438.t Executable file
View file

@ -0,0 +1,94 @@
#! /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 => 11;
# Create the rc file.
if (open my $fh, '>', 'bug.rc')
{
print $fh "data.location=.\n",
"dateformat=SNHDMY\n",
"report.foo.columns=entry,start,end,description\n",
"report.foo.dateformat=SNHDMY\n";
close $fh;
ok (-r 'bug.rc', 'Created bug.rc');
}
# Bug #438: Reports sorting by end, start, and entry are ordered incorrectly, if
# time is included.
# Ensure the two tasks have a 1 second delta in entry.
qx{../task rc:bug.rc add older};
sleep 1;
qx{../task rc:bug.rc add newer};
my $output = qx{../task rc:bug.rc rc.report.foo.sort:entry+ foo};
like ($output, qr/older.+newer/ms, 'sort:entry+ -> older newer');
$output = qx{../task rc:bug.rc rc.report.foo.sort:entry- foo};
like ($output, qr/newer.+older/ms, 'sort:entry- -> newer older');
# Ensure the two tasks have a 1 second delta in start.
qx{../task rc:bug.rc start 1};
sleep 1;
qx{../task rc:bug.rc start 2};
$output = qx{../task rc:bug.rc rc.report.foo.sort:start+ foo};
like ($output, qr/older.+newer/ms, 'sort:start+ -> older newer');
$output = qx{../task rc:bug.rc rc.report.foo.sort:start- foo};
like ($output, qr/newer.+older/ms, 'sort:start- -> newer older');
# Ensure the two tasks have a 1 second delta in end.
qx{../task rc:bug.rc done 1};
sleep 1;
qx{../task rc:bug.rc done 2};
$output = qx{../task rc:bug.rc rc.report.foo.sort:end+ foo};
like ($output, qr/older.+newer/ms, 'sort:end+ -> older newer');
$output = qx{../task rc:bug.rc rc.report.foo.sort:end- foo};
like ($output, qr/newer.+older/ms, 'sort:end- -> newer older');
# 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 'bug.rc';
ok (!-r 'bug.rc', 'Removed bug.rc');
exit 0;

View file

@ -34,7 +34,7 @@ use Test::More tests => 7;
if (open my $fh, '>', 'hook.rc')
{
print $fh "data.location=.\n",
"report.long.columns=id,project,priority,entry,start_time,due,",
"report.long.columns=id,project,priority,entry,start,due,",
"recur,countdown_compact,age,depends,tags,description\n",
"hooks=on\n",
"hook.format-countdown_compact=" . $ENV{'PWD'} . "/hook:countdown_compact\n";

View file

@ -1,84 +0,0 @@
#! /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 => 7;
# Create the rc file.
if (open my $fh, '>', 'hook.rc')
{
print $fh "data.location=.\n",
"report.completed.columns=end_time,project,priority,age,description\n",
"report.completed.sort=end_time+,priority-,project+\n",
"hooks=on\n",
"hook.format-end_time=" . $ENV{'PWD'} . "/hook:end_time\n";
close $fh;
ok (-r 'hook.rc', 'Created hook.rc');
}
# Create the hook functions.
if (open my $fh, '>', 'hook')
{
print $fh "function end_time (name, value)\n",
" value = '<' .. value .. '>'\n",
" return value, 0, nil\n",
"end\n";
close $fh;
ok (-r 'hook', 'Created hook');
}
my $output = qx{../task rc:hook.rc version};
if ($output =~ /PUC-Rio/)
{
qx{../task rc:hook.rc add foo};
qx{../task rc:hook.rc do 1};
$output = qx{../task rc:hook.rc completed};
like ($output, qr/<\d+\/\d+\/\d+\s\d+:\d+:\d+>/, 'format-end_time hook end_time -> <end_time>');
}
else
{
pass ('format-end_time hook end_time -> <end_time> - skip: no Lua support');
}
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'undo.data';
ok (!-r 'undo.data', 'Removed undo.data');
unlink 'hook';
ok (!-r 'hook', 'Removed hook');
unlink 'hook.rc';
ok (!-r 'hook.rc', 'Removed hook.rc');
exit 0;

View file

@ -1,82 +0,0 @@
#! /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 => 7;
# Create the rc file.
if (open my $fh, '>', 'hook.rc')
{
print $fh "data.location=.\n",
"report.long.columns=id,project,priority,entry_time,start,due,recur,countdown,age,depends,tags,description\n",
"hooks=on\n",
"hook.format-entry_time=" . $ENV{'PWD'} . "/hook:entry_time\n";
close $fh;
ok (-r 'hook.rc', 'Created hook.rc');
}
# Create the hook functions.
if (open my $fh, '>', 'hook')
{
print $fh "function entry_time (name, value)\n",
" value = '<' .. value .. '>'\n",
" return value, 0, nil\n",
"end\n";
close $fh;
ok (-r 'hook', 'Created hook');
}
my $output = qx{../task rc:hook.rc version};
if ($output =~ /PUC-Rio/)
{
qx{../task rc:hook.rc add foo};
$output = qx{../task rc:hook.rc long};
like ($output, qr/<\d+\/\d+\/\d+\s\d+:\d+:\d+>/, 'format-entry_time hook entry_time -> <entry_time>');
}
else
{
pass ('format-entry_time hook entry_time -> <entry_time> - skip: no Lua support');
}
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'undo.data';
ok (!-r 'undo.data', 'Removed undo.data');
unlink 'hook';
ok (!-r 'hook', 'Removed hook');
unlink 'hook.rc';
ok (!-r 'hook.rc', 'Removed hook.rc');
exit 0;

View file

@ -1,83 +0,0 @@
#! /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 => 7;
# Create the rc file.
if (open my $fh, '>', 'hook.rc')
{
print $fh "data.location=.\n",
"report.long.columns=id,project,priority,entry,start_time,due,recur,countdown,age,depends,tags,description\n",
"hooks=on\n",
"hook.format-start_time=" . $ENV{'PWD'} . "/hook:start_time\n";
close $fh;
ok (-r 'hook.rc', 'Created hook.rc');
}
# Create the hook functions.
if (open my $fh, '>', 'hook')
{
print $fh "function start_time (name, value)\n",
" value = '<' .. value .. '>'\n",
" return value, 0, nil\n",
"end\n";
close $fh;
ok (-r 'hook', 'Created hook');
}
my $output = qx{../task rc:hook.rc version};
if ($output =~ /PUC-Rio/)
{
qx{../task rc:hook.rc add foo};
qx{../task rc:hook.rc start 1};
$output = qx{../task rc:hook.rc long};
like ($output, qr/<\d+\/\d+\/\d+\s\d+:\d+:\d+>/, 'format-start_time hook start_time -> <start_time>');
}
else
{
pass ('format-start_time hook start_time -> <start_time> - skip: no Lua support');
}
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'undo.data';
ok (!-r 'undo.data', 'Removed undo.data');
unlink 'hook';
ok (!-r 'hook', 'Removed hook');
unlink 'hook.rc';
ok (!-r 'hook.rc', 'Removed hook.rc');
exit 0;