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

@ -5,11 +5,11 @@
+ Added feature #189, that records the start and stop times
as an annotation for a task.
+ Added feature #423, now custom report filters allow rc overrides.
+ Added feature #428, preparing the new structure for the NEWS file.
+ Added feature #429, which improves the 'all' report to exclude deleted.
tasks, provide a new sort order and include the 'end' column.
+ Added feature #431, which improves feedback after running the 'log'
command.
+ Added feature #428, preparing the new structure for the NEWS file.
+ New 'depends' column for custom reports.
+ New 'blocked' report for showing blocked tasks.
+ Improved man pages (thanks to Andy Lester).
@ -18,7 +18,9 @@
+ Fixed bug #427, preventing the task edit command to parse annotation
dates with spaces.
+ Fixed bug #433, making task command output more consistent.
+ Fixed bug #434, allowing users to complete tasks with status 'waiting'.
+ Fixed bug #434, allowing users to complete tasks with status 'waiting'
+ Fixed bug #438, correcting the sorting of the entry_time, start_time
and end_time columns (thanks to Michelle Crane).
+ Fixed bug #439, which ignored dateformat.annotation for sparse annotations.
------ old releases ------------------------------

8
NEWS
View file

@ -17,6 +17,14 @@ New configuration options in task 1.9.3
- journal.time, journal.time.start.annotation, journal.time.stop.annotation
-
Newly deprecated features in task 1.9.3
- entry_time, start_time and end_time are now synonyms for the entry, start
and end fields. Recent enhancements to date formatting render these fields
obsolete. The 'task show' command warns of the use of these deprecated
fields. The synonyms will be removed in a future version of task.
---
Task has been built and tested on the following configurations:

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;