Merge branch '2.4.5' of ssh://git.tasktools.org/tm/task into 2.4.5

This commit is contained in:
Paul Beckingham 2015-06-30 07:54:22 -04:00
commit 5d02291077
36 changed files with 324 additions and 750 deletions

View file

@ -24,7 +24,7 @@
and enabling more flexible use of the function.
- Enable "task sync" support by default. "cmake -DENABLE_SYNC=OFF" allows
disabling it and building Taskwarrior without libgnutls available.
- An attempt to add or remove a virtual tag is now an error (thanks to Scott M).
------ current release ---------------------------

6
NEWS
View file

@ -2,6 +2,7 @@
New Features in Taskwarrior 2.4.5
- The active context, if one is set, is now identified in "task context list"
- It is an error to attempt and add or remove of a virtual tag.
New commands in Taskwarrior 2.4.5
@ -12,6 +13,11 @@ Newly deprecated features in Taskwarrior 2.4.5
Removed features in 2.4.5
- The script 'context' was removed, now that context is a core feature.
- Non­extended forms of ISO-8601 date/time support is removed. This means
that 'YYYYMMDD' is no longer supported, but 'YYYY-MM-DD' is. For times,
'hhmmss' is no longer supported, but 'hh:mm:ss' is. The non-enxtended
forms all contain sequences of digits that make the identification of
IDs, UUIDs, and various date/time formats problematic.
Known Issues

View file

@ -506,7 +506,8 @@ Shows a report of aggregated task status by project.
.B task <filter> tags
Show a list of all tags used. Any special tags used are highlighted. Note that
virtual tags are not listed - they don't really exist, and are just a convenient
notation for other task metadata.
notation for other task metadata. It is an error to attempt to add or remove a
virtual tag.
.TP
.B task timesheet [weeks]
@ -666,7 +667,8 @@ are:
DELETED Matches if the task has deleted status
You can use +BLOCKED to filter blocked tasks, or -BLOCKED for unblocked tasks.
Similarly, -BLOCKED is equivalent to +UNBLOCKED.
Similarly, -BLOCKED is equivalent to +UNBLOCKED. It is an error to attempt to
add or remove a virtual tag.
.TP
.B project:<project-name>
@ -905,7 +907,7 @@ task ... due:7/14/2008
.TP
ISO-8601
task ... due:20130314T223000Z
task ... due:2013-03-14T22:30:00Z
.TP
Relative wording

View file

@ -340,7 +340,33 @@ void CLI2::lexArguments ()
{
// Note: Starts interating at index 1, because ::handleArg0 has already
// processed it.
bool terminated = false;
for (unsigned int i = 1; i < _original_args.size (); ++i)
{
// The terminator itself is captured.
if (_original_args[i] == "--")
{
terminated = true;
_args.push_back (A2 (_original_args[i], Lexer::Type::separator));
}
// Any arguments that are after the terminator are captured as words.
else if (terminated)
{
A2 word (_original_args[i], Lexer::Type::word);
word.tag ("TERMINATED");
_args.push_back (word);
}
// rc:<file> and rc.<name>[:=]<value> argumenst are captured whole.
else if (_original_args[i].substr (0, 3) == "rc:" ||
_original_args[i].substr (0, 3) == "rc.")
{
_args.push_back (A2 (_original_args[i], Lexer::Type::pair));
}
// Everything else gets lexed.
else
{
std::string lexeme;
Lexer::Type type;
@ -350,6 +376,7 @@ void CLI2::lexArguments ()
while (lex.token (lexeme, type))
_args.push_back (A2 (lexeme, type));
}
}
if (context.config.getInteger ("debug.parser") >= 3)
context.debug (dump ("CLI2::analyze lexArguments"));
@ -414,51 +441,6 @@ void CLI2::analyze ()
context.debug (dump ("CLI2::analyze end"));
}
/*
////////////////////////////////////////////////////////////////////////////////
// There are situations where a context filter is applied. This method
// determines whether one applies, and if so, applies it. Disqualifiers include:
// - filter contains ID or UUID
void CLI2::addContextFilter ()
{
// Detect if any context is set, and bail out if not
std::string contextName = context.config.get ("context");
if (contextName == "")
{
context.debug ("No context applied.");
return;
}
// Detect if UUID or ID is set, and bail out
for (auto& a : _args)
{
// TODO This looks wrong.
if (a.hasTag ("FILTER") &&
a.hasTag ("ATTRIBUTE") &&
! a.hasTag ("TERMINATED") &&
! a.hasTag ("WORD") &&
(a.attribute ("raw") == "id" || a.attribute ("raw") == "uuid"))
{
context.debug (format ("UUID/ID lexeme found '{1}', not applying context.", a.attribute ("raw")));
return;
}
}
// Apply context
context.debug ("Applying context: " + contextName);
std::string contextFilter = context.config.get ("context." + contextName);
if (contextFilter == "")
context.debug ("Context '" + contextName + "' not defined.");
else
{
addRawFilter ("( " + contextFilter + " )");
if (context.verbose ("context"))
context.footnote (format ("Context '{1}' set. Use 'task context none' to remove.", contextName));
}
}
*/
////////////////////////////////////////////////////////////////////////////////
// Process raw string.
void CLI2::addFilter (const std::string& arg)
@ -480,6 +462,52 @@ void CLI2::addFilter (const std::string& arg)
analyze ();
}
////////////////////////////////////////////////////////////////////////////////
// There are situations where a context filter is applied. This method
// determines whether one applies, and if so, applies it. Disqualifiers include:
// - filter contains ID or UUID
void CLI2::addContextFilter ()
{
// Detect if any context is set, and bail out if not
std::string contextName = context.config.get ("context");
if (contextName == "")
{
context.debug ("No context applied.");
return;
}
/*
// Detect if UUID or ID is set, and bail out
for (auto& a : _args)
{
// TODO This is needed, but the parsing is not yet complete, so the logic
// below is not valid.
if (a.hasTag ("FILTER") &&
a.hasTag ("ATTRIBUTE") &&
! a.hasTag ("TERMINATED") &&
! a.hasTag ("WORD") &&
(a.attribute ("raw") == "id" || a.attribute ("raw") == "uuid"))
{
context.debug (format ("UUID/ID lexeme found '{1}', not applying context.", a.attribute ("raw")));
return;
}
}
*/
// Apply context
context.debug ("Applying context: " + contextName);
std::string contextFilter = context.config.get ("context." + contextName);
if (contextFilter == "")
context.debug ("Context '" + contextName + "' not defined.");
else
{
addFilter (contextFilter);
if (context.verbose ("context"))
context.footnote (format ("Context '{1}' set. Use 'task context none' to remove.", contextName));
}
}
////////////////////////////////////////////////////////////////////////////////
// Parse the command line, identifiying filter components, expanding syntactic
// sugar as necessary.
@ -489,6 +517,9 @@ void CLI2::prepareFilter (bool applyContext)
_id_ranges.clear ();
_uuid_list.clear ();
if (applyContext)
addContextFilter ();
// Classify FILTER and MODIFICATION args, based on CMD and READCMD/WRITECMD.
bool changes = false;
bool foundCommand = false;
@ -711,13 +742,15 @@ void CLI2::aliasExpansion ()
for (auto& i : _args)
{
raw = i.attribute ("raw");
if (_aliases.find (raw) != _aliases.end ())
if (i.hasTag ("TERMINATED"))
{
reconstructed.push_back (i);
}
else if (_aliases.find (raw) != _aliases.end ())
{
for (auto& l : Lexer::split (_aliases[raw]))
{
A2 a (l, Lexer::Type::word);
a.tag ("ALIAS");
a.tag ("LEX");
reconstructed.push_back (a);
}
@ -725,15 +758,25 @@ void CLI2::aliasExpansion ()
changes = true;
}
else
{
reconstructed.push_back (i);
}
}
_args = reconstructed;
std::vector <std::string> reconstructedOriginals;
bool terminated = false;
for (auto& i : _original_args)
{
if (_aliases.find (i) != _aliases.end ())
if (i == "--")
terminated = true;
if (terminated)
{
reconstructedOriginals.push_back (i);
}
else if (_aliases.find (i) != _aliases.end ())
{
for (auto& l : Lexer::split (_aliases[i]))
reconstructedOriginals.push_back (l);
@ -742,8 +785,10 @@ void CLI2::aliasExpansion ()
changes = true;
}
else
{
reconstructedOriginals.push_back (i);
}
}
_original_args = reconstructedOriginals;
}

View file

@ -73,10 +73,8 @@ public:
void add (const std::string&);
void analyze ();
/*
void addContextFilter ();
*/
void addFilter (const std::string& arg);
void addContextFilter ();
void prepareFilter (bool applyContext = true);
const std::vector <std::string> getWords (bool filtered = true);
bool canonicalize (std::string&, const std::string&, const std::string&) const;

View file

@ -73,7 +73,8 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
context.timer_filter.start ();
_startCount = (int) input.size ();
// context.cli2.prepareFilter (applyContext);
context.cli2.prepareFilter (applyContext);
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
for (auto& a : context.cli2._args)
if (a.hasTag ("FILTER"))
@ -118,6 +119,8 @@ void Filter::subset (std::vector <Task>& output, bool applyContext /* = true */)
{
context.timer_filter.start ();
context.cli2.prepareFilter (applyContext);
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
for (auto& a : context.cli2._args)
if (a.hasTag ("FILTER"))
@ -179,7 +182,7 @@ void Filter::subset (std::vector <Task>& output, bool applyContext /* = true */)
}
else
{
safety (precompiled.size ());
safety ();
context.timer_filter.stop ();
for (auto& task : context.tdb2.pending.get_tasks ())
@ -258,15 +261,13 @@ bool Filter::pendingOnly ()
////////////////////////////////////////////////////////////////////////////////
// Disaster avoidance mechanism. If a WRITECMD has no filter, then it can cause
// all tasks to be modified. This is usually not intended.
void Filter::safety (unsigned int terms)
void Filter::safety ()
{
for (auto& a : context.cli2._args)
{
if (a.hasTag ("CMD"))
{
if (a.hasTag ("WRITECMD"))
{
if (terms)
{
if (! context.config.getBoolean ("allow.empty.filter"))
throw std::string (STRING_TASK_SAFETY_ALLOW);
@ -279,7 +280,6 @@ void Filter::safety (unsigned int terms)
// Sounds the alarm.
throw std::string (STRING_TASK_SAFETY_FAIL);
}
}
// CMD was found.
return;

View file

@ -43,7 +43,7 @@ public:
void subset (const std::vector <Task>&, std::vector <Task>&, bool applyContext = true);
void subset (std::vector <Task>&, bool applyContext = true);
bool pendingOnly ();
void safety (unsigned int);
void safety ();
private:
int _startCount;

View file

@ -61,16 +61,9 @@ void ISO8601d::ambiguity (bool value)
// | date-ext 'T' time-ext offset-ext # Specified TZ
// | date-ext 'T' time-ext # Local
// | date-ext # Local
// | date 'T' time 'Z'
// | date 'T' time offset-ext
// | date 'T' time
// | date
// | time-ext 'Z'
// | time-ext offset-ext Not needed
// | time-ext
// | time 'Z'
// | time offset
// | time
// ;
//
// date-ext ::= ±YYYYY-MM-DD Νot needed
@ -83,37 +76,14 @@ void ISO8601d::ambiguity (bool value)
// | YYYY-Www
// ;
//
// date ::= ±YYYYYMMDD Νot needed
// | ±YYYYYWwwD Νot needed
// | ±YYYYYWww Νot needed
// | ±YYYYYDDD Νot needed
// | ±YYYYYMM Νot needed
// | ±YYYYY Νot needed
// | ±YYY Νot needed
// | YYYYMMDD Ambiguous (number)
// | YYYYWwwD
// | YYYYWww
// | YYYYDDD Ambiguous (number)
// | YYYY-MM
// | YYYY Ambiguous (number)
// | YY Ambiguous (number)
// ;
//
// time-ext ::= hh:mm:ss[,ss]
// | hh:mm[,mm]
// | hh[,hh] Ambiguous (number)
// ;
//
// time ::= hhmmss[,ss] Ambiguous (number)
// | hhmm[,mm] Ambiguous (number)
// | hh[,hh] Ambiguous (number)
// ;
//
// time-utc-ext ::= hh:mm[:ss] 'Z' ;
// time-utc ::= hh[mm[ss]] 'Z' ;
//
// offset-ext ::= ±hh[:mm] ;
// offset ::= ±hh[mm] ;
//
// Not yet supported:
//
@ -136,12 +106,7 @@ bool ISO8601d::parse (const std::string& input, std::string::size_type& start)
parse_date_ext (n) ||
parse_time_utc_ext (n) ||
parse_time_off_ext (n) ||
parse_date_time (n) ||
parse_date (n, _ambiguity) ||
parse_time_utc (n) ||
parse_time_off (n) ||
parse_time_ext (n) || // Time last, as it is the most permissive.
parse_time (n, _ambiguity))
parse_time_ext (n)) // Time last, as it is the most permissive.
{
// Check the values and determine time_t.
if (validate ())
@ -214,47 +179,6 @@ bool ISO8601d::parse_date_time_ext (Nibbler& n)
return false;
}
////////////////////////////////////////////////////////////////////////////////
// date 'T' time 'Z'
// date 'T' time offset
// date 'T' time
bool ISO8601d::parse_date_time (Nibbler& n)
{
Nibbler backup (n);
if (parse_date (n, true))
{
if (n.skip ('T') &&
parse_time (n, true))
{
if (n.skip ('Z'))
{
_utc = true;
if (!Lexer::isDigit (n.next ()))
return true;
}
else if (parse_off (n))
{
if (!Lexer::isDigit (n.next ()))
return true;
}
if (!Lexer::isDigit (n.next ()))
return true;
}
// Restore date
_year = 0;
_month = 0;
_week = 0;
_weekday = 0;
_julian = 0;
_day = 0;
}
n = backup;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// YYYY-MM-DD
// YYYY-DDD
@ -303,64 +227,6 @@ bool ISO8601d::parse_date_ext (Nibbler& n)
return false;
}
////////////////////////////////////////////////////////////////////////////////
// YYYYMMDD Ambiguous (number)
// YYYYWwwD
// YYYYWww
// YYYYDDD Ambiguous (number)
// YYYY-MM
bool ISO8601d::parse_date (Nibbler& n, bool ambiguous)
{
Nibbler backup (n);
int year;
if (n.getDigit4 (year))
{
int month;
if (n.skip ('W'))
{
int week;
if (n.getDigit2 (week))
{
_week = week;
int day;
if (n.getDigit (day))
_weekday = day;
_year = year;
if (!Lexer::isDigit (n.next ()))
return true;
}
}
else if (n.skip ('-'))
{
if (n.getDigit2 (_month))
{
_year = year;
if (!Lexer::isDigit (n.next ()))
return true;
}
}
else if (n.getDigit4 (month))
{
_year = year;
_month = month / 100;
_day = month % 100;
if (!Lexer::isDigit (n.next ()))
return true;
}
else if (ambiguous && n.getDigit3 (_julian))
{
_year = year;
if (!Lexer::isDigit (n.next ()))
return true;
}
}
n = backup;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// ±hh[:mm]
bool ISO8601d::parse_off_ext (Nibbler& n)
@ -394,37 +260,7 @@ bool ISO8601d::parse_off_ext (Nibbler& n)
}
////////////////////////////////////////////////////////////////////////////////
// ±hh[mm]
bool ISO8601d::parse_off (Nibbler& n)
{
Nibbler backup (n);
std::string sign;
if (n.getN (1, sign))
{
if (sign == "+" || sign == "-")
{
int offset;
int hh;
if (n.getDigit2 (hh))
{
offset = hh * 3600;
int mm;
if (n.getDigit2 (mm))
offset += mm * 60;
_offset = (sign == "-") ? -offset : offset;
if (!Lexer::isDigit (n.next ()))
return true;
}
}
}
n = backup;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// hh[:mm[:ss]]
// hh:mm[:ss]
bool ISO8601d::parse_time_ext (Nibbler& n)
{
Nibbler backup (n);
@ -433,20 +269,15 @@ bool ISO8601d::parse_time_ext (Nibbler& n)
int mm;
int ss;
if (n.getDigit2 (hh) &&
!n.getDigit (mm))
n.skip (':') &&
n.getDigit2 (mm))
{
seconds = hh * 3600;
if (n.skip (':') &&
n.getDigit2 (mm) &&
!n.getDigit (ss))
{
seconds += mm * 60;
seconds = (hh * 3600) + (mm * 60);
if (n.skip (':') &&
n.getDigit2 (ss))
{
seconds += ss;
_seconds = seconds;
return true;
}
@ -463,35 +294,6 @@ bool ISO8601d::parse_time_ext (Nibbler& n)
return false;
}
////////////////////////////////////////////////////////////////////////////////
// hhmm[ss]
bool ISO8601d::parse_time (Nibbler& n, bool ambiguous)
{
if (!ambiguous)
return false;
Nibbler backup (n);
int seconds = 0;
int hh;
int mm;
if (n.getDigit2 (hh) &&
n.getDigit2 (mm))
{
seconds = hh * 3600 + mm * 60;
int ss;
if (n.getDigit2 (ss))
seconds += ss;
_seconds = seconds;
if (!Lexer::isDigit (n.next ()))
return true;
}
n = backup;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// time-ext 'Z'
bool ISO8601d::parse_time_utc_ext (Nibbler& n)
@ -509,23 +311,6 @@ bool ISO8601d::parse_time_utc_ext (Nibbler& n)
return false;
}
////////////////////////////////////////////////////////////////////////////////
// time 'Z'
bool ISO8601d::parse_time_utc (Nibbler& n)
{
n.save ();
if (parse_time (n, true) &&
n.skip ('Z'))
{
_utc = true;
if (!Lexer::isDigit (n.next ()))
return true;
}
n.restore ();
return false;
}
////////////////////////////////////////////////////////////////////////////////
// time-ext offset-ext
bool ISO8601d::parse_time_off_ext (Nibbler& n)
@ -542,22 +327,6 @@ bool ISO8601d::parse_time_off_ext (Nibbler& n)
return false;
}
////////////////////////////////////////////////////////////////////////////////
// time offset
bool ISO8601d::parse_time_off (Nibbler& n)
{
Nibbler backup (n);
if (parse_time (n, true) &&
parse_off (n))
{
if (!Lexer::isDigit (n.next ()))
return true;
}
n = backup;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Using Zeller's Congruence.
int ISO8601d::dayOfWeek (int year, int month, int day)

View file

@ -45,17 +45,11 @@ public:
private:
bool parse_date_time_ext (Nibbler&);
bool parse_date_time (Nibbler&);
bool parse_date_ext (Nibbler&);
bool parse_date (Nibbler&, bool);
bool parse_off_ext (Nibbler&);
bool parse_off (Nibbler&);
bool parse_time_ext (Nibbler&);
bool parse_time (Nibbler&, bool);
bool parse_time_utc_ext (Nibbler&);
bool parse_time_utc (Nibbler&);
bool parse_time_off_ext (Nibbler&);
bool parse_time_off (Nibbler&);
int dayOfWeek (int, int, int);
bool validate ();
void resolve ();

View file

@ -324,6 +324,15 @@ void Lexer::dequote (std::string& input)
}
}
////////////////////////////////////////////////////////////////////////////////
bool Lexer::wasQuoted (const std::string& input)
{
if (input.find_first_of (" \t()") != std::string::npos)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Lexer::isEOS () const
{

View file

@ -75,6 +75,7 @@ public:
static bool isBoundary (int, int);
static bool isPunctuation (int);
static void dequote (std::string&);
static bool wasQuoted (const std::string&);
// Helpers.
bool isEOS () const;

View file

@ -1092,6 +1092,7 @@ bool Task::hasTag (const std::string& tag) const
{
// Synthetic tags - dynamically generated, but do not occupy storage space.
// Note: This list must match that in CmdInfo::execute.
// Note: This list must match that in ::feedback_reserved_tags.
if (tag == "BLOCKED") return is_blocked;
if (tag == "UNBLOCKED") return !is_blocked;
if (tag == "BLOCKING") return is_blocking;
@ -2094,11 +2095,13 @@ void Task::modify (modType type, bool text_required /* = false */)
else if (a._lextype == Lexer::Type::tag)
{
std::string tag = a.attribute ("name");
feedback_reserved_tags (tag);
if (a.attribute ("sign") == "+")
{
context.debug (label + "tags <-- add '" + tag + "'");
addTag (tag);
feedback_special_tags ((*this), tag);
feedback_special_tags (*this, tag);
}
else
{

View file

@ -63,8 +63,7 @@ int CmdCalc::execute (std::string& output)
// Compile all the args into one expression.
std::string expression;
std::vector <std::string> words = context.cli2.getWords ();
for (auto& word : words)
for (auto& word : context.cli2.getWords ())
expression += word + " ";
// Evaluate according to preference.

View file

@ -81,10 +81,7 @@ int CmdCustom::execute (std::string& output)
// Add the report filter to any existing filter.
if (reportFilter != "")
{
context.cli2.addFilter (reportFilter);
context.cli2.prepareFilter ();
}
// Apply filter.
handleRecurrence ();

View file

@ -303,6 +303,7 @@ int CmdInfo::execute (std::string& output)
// Virtual tags.
{
// Note: This list must match that in Task::hasTag.
// Note: This list must match that in ::feedback_reserved_tags.
std::string virtualTags = "";
if (task.hasTag ("ACTIVE")) virtualTags += "ACTIVE ";
if (task.hasTag ("ANNOTATED")) virtualTags += "ANNOTATED ";

View file

@ -351,6 +351,41 @@ void feedback_affected (const std::string& effect, const Task& task)
}
}
////////////////////////////////////////////////////////////////////////////////
// Implements feedback and error when adding a reserved tag name.
void feedback_reserved_tags (const std::string& tag)
{
// Note: This list must match that in Task::hasTag.
// Note: This list must match that in CmdInfo::execute.
if (tag == "BLOCKED" ||
tag == "UNBLOCKED" ||
tag == "BLOCKING" ||
tag == "READY" ||
tag == "DUE" ||
tag == "DUETODAY" ||
tag == "TODAY" ||
tag == "YESTERDAY" ||
tag == "TOMORROW" ||
tag == "OVERDUE" ||
tag == "WEEK" ||
tag == "MONTH" ||
tag == "YEAR" ||
tag == "ACTIVE" ||
tag == "SCHEDULED" ||
tag == "CHILD" ||
tag == "UNTIL" ||
tag == "ANNOTATED" ||
tag == "TAGGED" ||
tag == "PARENT" ||
tag == "WAITING" ||
tag == "PENDING" ||
tag == "COMPLETED" ||
tag == "DELETED")
{
throw format (STRING_FEEDBACK_TAG_VIRTUAL, tag);
}
}
////////////////////////////////////////////////////////////////////////////////
// Implements feedback when adding special tags to a task.
void feedback_special_tags (const Task& task, const std::string& tag)

View file

@ -792,6 +792,7 @@
#define STRING_FEEDBACK_TAG_NONAG "Das besondere Schlagwort 'nonag' verhindert Nachfragen, wenn diese Aufgabe geändert wird."
#define STRING_FEEDBACK_TAG_NOCAL "Das besondere Schlagwort 'nocal' verhindert, dass diese Aufgabe im 'calendar'-Report erscheint."
#define STRING_FEEDBACK_TAG_NEXT "Das besondere Schlagwort 'next' erhöht die Dringlichkeit dieser Aufgabe, sodass sie im 'next'-Report erscheint."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Aufgabe {1} '{2}' entsperrt."
#define STRING_FEEDBACK_EXPIRED "Aufgabe {1} '{2}' ist abgelaufen und wurde gelöscht."
#define STRING_FEEDBACK_BACKLOG "Lokale Änderungen. Datenabgleich erforderlich."

View file

@ -372,7 +372,7 @@
#define STRING_CMD_UDAS_ORPHANS "{1} Orphan UDAs"
#define STRING_CMD_DELETE_USAGE "Deletes the specified task"
#define STRING_CMD_DELETE_CONFIRM "Permanently delete task {1} '{2}'?"
#define STRING_CMD_DELETE_CONFIRM "Delete task {1} '{2}'?"
#define STRING_CMD_DELETE_TASK "Deleting task {1} '{2}'."
#define STRING_CMD_DELETE_TASK_R "Deleting recurring task {1} '{2}'."
#define STRING_CMD_DELETE_CONFIRM_R "This is a recurring task. Do you want to delete all pending recurrences of this same task?"
@ -792,6 +792,7 @@
#define STRING_FEEDBACK_TAG_NONAG "The 'nonag' special tag will prevent nagging when this task is modified."
#define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the 'calendar' report."
#define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Task {1} '{2}' expired and was deleted."
#define STRING_FEEDBACK_BACKLOG "There are local changes. Sync required."

View file

@ -792,6 +792,7 @@
#define STRING_FEEDBACK_TAG_NONAG "Speciala etikedo 'nonag' antaŭmalebligitos molestojn, kiam oni modifus tiun taskon."
#define STRING_FEEDBACK_TAG_NOCAL "Speciala etikedo 'nocal' ekskluzivos tiun taskon ĉe raporto 'calendar'."
#define STRING_FEEDBACK_TAG_NEXT "Speciala etikedo 'next' pligrandigos la urĝecon de tiu tasko por ke ĝi aperus ĉe raporto 'next'."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Malblokis {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Tasko {1} '{2}' fortempiĝis do estis viŝata."
#define STRING_FEEDBACK_BACKLOG "Estas lokaj ŝanĝoj. Sinkronigo devita."

View file

@ -804,6 +804,7 @@
#define STRING_FEEDBACK_TAG_NONAG "La marca especial 'nonag' evitará el recuerdo fastidioso cuando la tarea sea modificada."
#define STRING_FEEDBACK_TAG_NOCAL "La marca especial 'nocal' mantendrá esta tarea fuera del informe 'calendar'."
#define STRING_FEEDBACK_TAG_NEXT "La etiqueta especial 'next' aumentará la urgencia de esta tarea para que aparezca en el informe 'next'."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Desbloqueada {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "La tarea {1} '{2}' caducó y fue eliminada."
#define STRING_FEEDBACK_BACKLOG "Hay modificaciones locales. Se require una sincronización."

View file

@ -792,6 +792,7 @@
#define STRING_FEEDBACK_TAG_NONAG "The 'nonag' special tag will prevent nagging when this task is modified."
#define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the 'calendar' report."
#define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Tâche {1} '{2}' a expiré et a été supprimée."
#define STRING_FEEDBACK_BACKLOG "Il y a des changements locaux. Synchronisation requise."

View file

@ -791,6 +791,7 @@
#define STRING_FEEDBACK_TAG_NONAG "Il tag speciale 'nonag' eviterà problemi quando il task è modificato."
#define STRING_FEEDBACK_TAG_NOCAL "Il tag speciale 'nocal' manterrà il task fuori dal report 'calendar'."
#define STRING_FEEDBACK_TAG_NEXT "Il tag speciale 'next' aumenterà l'urgenza di questo task in modo che appaia nel report 'next'."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Sbloccato {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Il task {1} '{2}' è scaduto ed è stato eliminato"
#define STRING_FEEDBACK_BACKLOG "There are local changes. Sync required."

View file

@ -792,6 +792,7 @@
#define STRING_FEEDBACK_TAG_NONAG "The 'nonag' special tag will prevent nagging when this task is modified."
#define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the 'calendar' report."
#define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Task {1} '{2}' expired and was deleted."
#define STRING_FEEDBACK_BACKLOG "There are local changes. Sync required."

View file

@ -792,6 +792,7 @@
#define STRING_FEEDBACK_TAG_NONAG "Specjalny tag 'nonag' uchroni przed upierdliwością kiedy zadanie jest modyfikowane."
#define STRING_FEEDBACK_TAG_NOCAL "Specjalny tag 'nocal' spowoduje nie dodawanie zadania do kalendarza."
#define STRING_FEEDBACK_TAG_NEXT "Specjalny tag 'next' podniesie pilność tego zadania co spowoduje wyświetlenie go w raporcie 'next'."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Odblokowane {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Zadanie {1} '{2}' jest przedawnione i zostało usunięte."
#define STRING_FEEDBACK_BACKLOG "Wykryto lokalne zmiany. Wymagana synchronizacja."

View file

@ -792,6 +792,7 @@
#define STRING_FEEDBACK_TAG_NONAG "A marca especial 'nonag' irá prevenir avisos quando a tarefa é modificada."
#define STRING_FEEDBACK_TAG_NOCAL "A marca especial 'nocal' irá manter esta tarefa ausente do relatório de 'calendário'."
#define STRING_FEEDBACK_TAG_NEXT "A marca especial 'next' irá aumentar a urgência desta tarefa de modo a que apareça no relatório 'next'."
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
#define STRING_FEEDBACK_UNBLOCKED "Desbloqueada {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Tarefa {1} '{2}' expirou e foi eliminada."
#define STRING_FEEDBACK_BACKLOG "Há modificações locais. Necessário sincronizar (sync)."

View file

@ -67,6 +67,7 @@ std::string renderAttribute (const std::string&, const std::string&, const std::
void feedback_affected (const std::string&);
void feedback_affected (const std::string&, int);
void feedback_affected (const std::string&, const Task&);
void feedback_reserved_tags (const std::string&);
void feedback_special_tags (const Task&, const std::string&);
void feedback_unblocked (const Task&);
void feedback_backlog ();

View file

@ -36,35 +36,29 @@ from basetest import Task, TestCase
class TestIDPosition(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
@classmethod
def setUpClass(cls):
"""Executed once before any test in the class"""
cls.t = Task()
self.t(("add", "one"))
self.t(("add", "two"))
self.t(("add", "three"))
def test_id(self):
"""Test id before and after command"""
code, out, err = self.t(("list",))
cls.t(("add", "one"))
cls.t(("add", "two"))
def test_id_read_cmd(self):
"""Test id before and after read command"""
code, out, err = self.t(("1", "info"))
self.assertIn("one", out)
self.assertIn("two", out)
self.assertIn("three", out)
self.assertNotIn("two", out)
code, out, err = self.t(("1", "done"))
self.assertIn("Completed 1 task.", out)
code, out, err = self.t(("info", "1"))
self.assertIn("one", out)
self.assertNotIn("two", out)
filter = "rc.allow.empty.filter:yes"
code, out, err = self.t.runError((filter, "done", "2"))
self.assertIn("Command prevented from running.", err)
self.assertNotIn("Completed 1 task.", out)
def test_id_write_cmd(self):
"""Test id before write command"""
code, out, err = self.t(("2", "done"))
self.assertIn("Completed task 2", out)
filter = "rc.allow.empty.filter:no"
code, out, err = self.t.runError((filter, "done", "2"))
self.assertIn("You did not specify a filter, and with the "
"'allow.empty.filter' value, no action is taken.", err)
self.assertNotIn("Completed 1 task.", out)
if __name__ == "__main__":
from simpletap import TAPTestRunner

View file

@ -84,17 +84,17 @@ $output = qx{../src/task rc:caseless.rc info 1 2>&1};
like ($output, qr/four five six/, 'one two three\nfour FIVE six -> /five/five/ = caseless succeed');
# Description filter.
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:yes ls One 2>&1};
unlike ($output, qr/one two three/, 'one two three\nfour five six -> ls One = fail');
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:yes ls /One/ 2>&1};
unlike ($output, qr/one two three/, 'one two three\nfour five six -> ls /One/ = fail');
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:no ls One 2>&1};
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:no ls /One/ 2>&1};
like ($output, qr/one two three/, 'one two three\nfour five six -> ls One caseless = succeed');
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:yes ls Five 2>&1};
unlike ($output, qr/four five six/, 'one two three\nfour five six -> ls Five = fail');
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:yes ls /Five/ 2>&1};
unlike ($output, qr/four five six/, 'one two three\nfour five six -> ls /Five/ = fail');
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:no ls Five 2>&1};
like ($output, qr/four five six/, 'one two three\nfour five six -> ls Five caseless = succeed');
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:no ls /Five/ 2>&1};
like ($output, qr/four five six/, 'one two three\nfour five six -> ls /Five/ caseless = succeed');
# Annotation filter.
$output = qx{../src/task rc:caseless.rc rc.search.case.sensitive:yes ls description.contains:Three 2>&1};

View file

@ -56,49 +56,49 @@ qx{../src/task rc:$rc add foo 2>&1} for 1..10;
# Test the various forms of "Yes".
my $output = qx{echo "Yes" | ../src/task rc:$rc 1 del 2>&1};
like ($output, qr/Permanently delete task 1 'foo'\? \(yes\/no\)/, 'confirmation - Yes works');
like ($output, qr/Delete task 1 'foo'\? \(yes\/no\)/, 'confirmation - Yes works');
unlike ($output, qr/Task not deleted\./, 'confirmation - Yes works');
$output = qx{echo "ye" | ../src/task rc:$rc 2 del 2>&1};
like ($output, qr/Permanently delete task 2 'foo'\? \(yes\/no\)/, 'confirmation - ye works');
like ($output, qr/Delete task 2 'foo'\? \(yes\/no\)/, 'confirmation - ye works');
unlike ($output, qr/Task not deleted\./, 'confirmation - ye works');
$output = qx{echo "y" | ../src/task rc:$rc 3 del 2>&1};
like ($output, qr/Permanently delete task 3 'foo'\? \(yes\/no\)/, 'confirmation - y works');
like ($output, qr/Delete task 3 'foo'\? \(yes\/no\)/, 'confirmation - y works');
unlike ($output, qr/Task not deleted\./, 'confirmation - y works');
$output = qx{echo "YES" | ../src/task rc:$rc 4 del 2>&1};
like ($output, qr/Permanently delete task 4 'foo'\? \(yes\/no\)/, 'confirmation - YES works');
like ($output, qr/Delete task 4 'foo'\? \(yes\/no\)/, 'confirmation - YES works');
unlike ($output, qr/Task not deleted\./, 'confirmation - YES works'); # 10
$output = qx{echo "YE" | ../src/task rc:$rc 5 del 2>&1};
like ($output, qr/Permanently delete task 5 'foo'\? \(yes\/no\)/, 'confirmation - YE works');
like ($output, qr/Delete task 5 'foo'\? \(yes\/no\)/, 'confirmation - YE works');
unlike ($output, qr/Task not deleted\./, 'confirmation - YE works');
$output = qx{echo "Y" | ../src/task rc:$rc 6 del 2>&1};
like ($output, qr/Permanently delete task 6 'foo'\? \(yes\/no\)/, 'confirmation - Y works');
like ($output, qr/Delete task 6 'foo'\? \(yes\/no\)/, 'confirmation - Y works');
unlike ($output, qr/Task not deleted\./, 'confirmation - Y works');
# Test the various forms of "no".
$output = qx{echo "no" | ../src/task rc:$rc 7 del 2>&1};
like ($output, qr/Permanently delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - no works');
like ($output, qr/Delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - no works');
like ($output, qr/Task not deleted\./, 'confirmation - no works');
$output = qx{echo "n" | ../src/task rc:$rc 7 del 2>&1};
like ($output, qr/Permanently delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - n works');
like ($output, qr/Delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - n works');
like ($output, qr/Task not deleted\./, 'confirmation - n works');
$output = qx{echo "NO" | ../src/task rc:$rc 7 del 2>&1};
like ($output, qr/Permanently delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - NO works');
like ($output, qr/Delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - NO works');
like ($output, qr/Task not deleted\./, 'confirmation - NO works'); # 20
$output = qx{echo "N" | ../src/task rc:$rc 7 del 2>&1};
like ($output, qr/Permanently delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - N works');
like ($output, qr/Delete task 7 'foo'\? \(yes\/no\)/, 'confirmation - N works');
like ($output, qr/Task not deleted\./, 'confirmation - N works');
# Test newlines.
$output = qx{cat response.txt | ../src/task rc:$rc 7 del 2>&1};
like ($output, qr/(Permanently delete task 7 'foo'\? \(yes\/no\)) \1 \1/, 'confirmation - \n re-prompt works'); # 43
like ($output, qr/(Delete task 7 'foo'\? \(yes\/no\)) \1 \1/, 'confirmation - \n re-prompt works'); # 43
# Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data response.txt), $rc;

View file

@ -103,38 +103,6 @@ class TestIncorrectDate(BaseDateTimeNegativeTest):
def test_set_incorrect_datetime_day_two_hundred_in_YYYY_WwwD(self):
self.assertInvalidDatetimeFormat('2014-W24200')
@unittest.expectedFailure
def test_set_incorrect_datetime_week_with_the_number_zero_in_YYYYWww(self):
self.assertInvalidDatetimeFormat('2014W00')
def test_set_incorrect_datetime_overflow_in_week_in_YYYYWww(self):
self.assertInvalidDatetimeFormat('2014W54')
@unittest.expectedFailure
def test_set_incorrect_datetime_week_zero_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W001')
@unittest.expectedFailure
def test_set_incorrect_datetime_fifth_day_of_week_zero_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W005')
def test_set_incorrect_datetime_overflow_week_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W541')
def test_set_incorrect_datetime_huge_overflow_week_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W991')
@unittest.expectedFailure
def test_set_incorrect_datetime_day_zero_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W240')
def test_set_incorrect_datetime_day_eight_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W248')
def test_set_incorrect_datetime_day_two_hundred_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W24200')
@unittest.expectedFailure
def test_set_incorrect_datetime_month_zero_in_YYYY_MM(self):
self.assertInvalidDatetimeFormat('2014-00')
@ -211,18 +179,15 @@ class TestIncorrectTime(BaseDateTimeNegativeTest):
def test_set_incorrect_datetime_negative_minutes_in_hh_mmZ(self):
self.assertInvalidDatetimeFormat('12:-12Z')
@unittest.expectedFailure
def test_set_incorrect_datetime_hour_overflow_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('24:00+01:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('99:00+01:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_minute_overflow_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:60+01:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:99+01:00')
@ -241,18 +206,15 @@ class TestIncorrectTime(BaseDateTimeNegativeTest):
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:-12+01:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_hour_overflow_in_hh_mm_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('24:00-01:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('99:00-01:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_minute_overflow_in_hh_mm_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:60-01:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:99-01:00')
@ -312,7 +274,6 @@ class TestIncorrectTime(BaseDateTimeNegativeTest):
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:-12:12')
@unittest.expectedFailure
def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:-12')
@ -445,7 +406,6 @@ class TestIncorrectTime(BaseDateTimeNegativeTest):
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:-12:12-01:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12:-12-01:00')
@ -505,22 +465,18 @@ class TestIncorrectTime(BaseDateTimeNegativeTest):
def test_set_incorrect_datetime_invalid_negative_offset_length_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12-3:2')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_hour_positive_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+13:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_medium_hour_positive_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+24:00')
def test_set_incorrect_datetime_invalid_huge_hour_positive_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+99:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_minute_positive_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+03:60')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_huge_minute_positive_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+03:99')
@ -533,22 +489,18 @@ class TestIncorrectTime(BaseDateTimeNegativeTest):
def test_set_incorrect_datetime_invalid_positive_offset_length_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+3:2')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_hour_negative_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-13:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_medium_hour_negative_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-24:00')
def test_set_incorrect_datetime_invalid_huge_hour_negative_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-99:00')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_minute_negative_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-03:60')
@unittest.expectedFailure
def test_set_incorrect_datetime_invalid_huge_minute_negative_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-03:99')

View file

@ -1,205 +1,63 @@
#! /usr/bin/env perl
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## 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.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
###############################################################################
#
# Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
#
# 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.
#
# http://www.opensource.org/licenses/mit-license.php
#
###############################################################################
use strict;
use warnings;
use Time::Local;
use Test::More tests => 34;
import sys
import os
import unittest
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# Ensure environment has no influence.
delete $ENV{'TASKDATA'};
delete $ENV{'TASKRC'};
from basetest import Task, TestCase
use File::Basename;
my $ut = basename ($0);
my $rc = $ut . '.rc';
# Create the rc file.
if (open my $fh, '>', $rc)
{
print $fh "data.location=.\n",
"confirmation=off\n";
close $fh;
}
class TestFeature891(TestCase):
@classmethod
def setUp(self):
self.t = Task()
self.t(("add", "one"))
self.t(("add", "two"))
# Feature 891: UUID filter should be uuid.endswith by default
# Create some example data directly. This is so that we have complete control
# over the UUID.
if (open my $fh, '>', 'pending.data')
{
my $timeA = timegm (00,00,12,22,11,2008);
my $timeB = timegm (00,00,12,17,03,2009);
print $fh <<EOF;
[description:"one" entry:"$timeA" status:"pending" uuid:"a7097693-91c2-4cbe-ba89-ddcc87e5582c"]
[description:"two" entry:"$timeB" status:"pending" uuid:"e8f72d91-964c-424b-8fd5-556434648b6b"]
EOF
code, self.uuid, err = self.t(("_get", "1.uuid"))
self.uuid = self.uuid.strip()
close $fh;
}
def test_uuid_filter(self):
for i in range(35,7,-1):
self.tap("UUID %s" % self.uuid[0:i])
code, out, err = self.t((self.uuid[0:i], "list"))
self.assertIn("one", out)
self.assertNotIn("two", out)
my $output = qx{../src/task rc:$rc 1 info 2>&1};
my ($uuid) = $output =~ /UUID\s+(\S{36})/ms;
# TODO This should fail because a 7-character UUID is not a UUID, but
# instead it blindly does nothing, and succeeds.
#code, out, err = self.t((self.uuid[0:6], "list"))
$output = qx{../src/task rc:$rc $uuid list 2>&1};
like ($output, qr/one/, "Found with $uuid");
my ($short) = $uuid =~ /^(.{35})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{34})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{33})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{32})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{31})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{30})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{29})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{28})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{27})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{26})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{25})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{24})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{23})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{22})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{21})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{20})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{19})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{18})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{17})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{16})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{15})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{14})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{13})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{12})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{11})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{10})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{9})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{8})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
like ($output, qr/one/, "Found with $short");
($short) = $uuid =~ /^(.{7})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
unlike ($output, qr/one/, "Not found with $short");
($short) = $uuid =~ /^(.{6})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
unlike ($output, qr/one/, "Not found with $short");
($short) = $uuid =~ /^(.{5})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
unlike ($output, qr/one/, "Not found with $short");
($short) = $uuid =~ /^(.{4})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
unlike ($output, qr/one/, "Not found with $short");
($short) = $uuid =~ /^(.{3})/;
$output = qx{../src/task rc:$rc $short list 2>&1};
unlike ($output, qr/one/, "Not found with $short");
# Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data), $rc;
exit 0;
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View file

@ -42,13 +42,13 @@ if (open my $fh, '>', 'bug.rc')
}
# Feature: variable to control printing of empty columns
qx{../src/task rc:bug.rc add sample desc 2>&1};
qx{../src/task rc:bug.rc add /sample/ desc 2>&1};
qx{../src/task rc:bug.rc add withP project:house 2>&1};
my $output = qx{../src/task test sample rc:bug.rc 2>&1};
my $output = qx{../src/task test /sample/ rc:bug.rc 2>&1};
unlike ($output, qr/Project/, 'empty \'project\' column is not printed by default');
$output = qx{../src/task test sample rc.print.empty.columns:yes rc:bug.rc 2>&1};
$output = qx{../src/task test /sample/ rc.print.empty.columns:yes rc:bug.rc 2>&1};
like ($output, qr/Project/, 'empty \'project\' column is printed if rc.print.empty.columns:yes');
$output = qx{../src/task test rc:bug.rc 2>&1};

View file

@ -269,8 +269,8 @@ class TestFilter(TestCase):
@unittest.expectedFailure
def test_regex_list_project(self):
"""filter - rc.regex:on list project:/[A-Z]/"""
code, out, err = self.t(("rc.regex:on", "list", "project:/[A-Z]/"))
"""filter - rc.regex:on list project~[A-Z]"""
code, out, err = self.t(("rc.regex:on", "list", "project~[A-Z]"))
self.assertIn("one", out)
self.assertIn("two", out)
@ -280,10 +280,9 @@ class TestFilter(TestCase):
self.assertNotIn("six", out)
self.assertNotIn("seven", out)
@unittest.expectedFailure
def test_regex_list_project_any(self):
"""filter - rc.regex:on list project:."""
code, out, err = self.t(("rc.regex:on", "list", "project:."))
"""filter - rc.regex:on list project~."""
code, out, err = self.t(("rc.regex:on", "list", "project~."))
self.assertIn("one", out)
self.assertIn("two", out)

View file

@ -33,9 +33,6 @@
Context context;
#define AMBIGUOUS // Include ambiguous forms
#undef AMBIGUOUS // Exclude ambiguous forms
////////////////////////////////////////////////////////////////////////////////
void testParse (
UnitTest& t,
@ -74,11 +71,7 @@ void testParse (
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (1214
#ifdef AMBIGUOUS
+ 48
#endif
);
UnitTest t (734);
ISO8601d iso;
std::string::size_type start = 0;
@ -139,7 +132,6 @@ int main (int argc, char** argv)
int hms = (12 * 3600) + (34 * 60) + 56; // The time 12:34:56 in seconds.
int hm = (12 * 3600) + (34 * 60); // The time 12:34:00 in seconds.
int h = (12 * 3600); // The time 12:00:00 in seconds.
int z = 3600; // TZ offset.
int ld = local_s > hms ? 86400 : 0; // Local extra day if now > hms.
@ -155,31 +147,12 @@ int main (int argc, char** argv)
// input i Year Mo Wk WD Jul Da Secs TZ UTC time_t
testParse (t, "12:34:56Z", 9, 0, 0, 0, 0, 0, 0, hms, 0, true, utc+hms+ud );
testParse (t, "12:34Z", 6, 0, 0, 0, 0, 0, 0, hm, 0, true, utc+hm+ud );
testParse (t, "12Z", 3, 0, 0, 0, 0, 0, 0, h, 0, true, utc+h+ud );
testParse (t, "12:34:56+01:00", 14, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud );
testParse (t, "12:34:56+01", 11, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud );
testParse (t, "12:34+01:00", 11, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud );
testParse (t, "12:34+01", 8, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud );
// testParse (t, "12+01:00", 8, 0, 0, 0, 0, 0, 0, h, 3600, false, utc+h-z+ud );
// testParse (t, "12+01", 5, 0, 0, 0, 0, 0, 0, h, 3600, false, utc+h-z+ud );
testParse (t, "12:34:56", 8, 0, 0, 0, 0, 0, 0, hms, 0, false, local+hms+ld );
testParse (t, "12:34", 5, 0, 0, 0, 0, 0, 0, hm, 0, false, local+hm+ld );
#ifdef AMBIGUOUS
testParse (t, "12", 2, 0, 0, 0, 0, 0, 0, h, 0, false, local+h+ld );
#endif
// time
// input i Year Mo Wk WD Jul Da Secs TZ UTC time_t
testParse (t, "123456Z", 7, 0, 0, 0, 0, 0, 0, hms, 0, true, utc+hms+ud );
testParse (t, "1234Z", 5, 0, 0, 0, 0, 0, 0, hm, 0, true, utc+hm+ud );
testParse (t, "123456+0100", 11, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud );
testParse (t, "123456+01", 9, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud );
testParse (t, "1234+0100", 9, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud );
testParse (t, "1234+01", 7, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud );
// testParse (t, "12+0100", 7, 0, 0, 0, 0, 0, 0, h, 3600, false, utc+h-z+ud );
#ifdef AMBIGUOUS
testParse (t, "123456", 6, 0, 0, 0, 0, 0, 0, hms, 0, false, local+hms+ld );
#endif
// datetime-ext
// input i Year Mo Wk WD Jul Da Secs TZ UTC time_t
@ -239,82 +212,6 @@ int main (int argc, char** argv)
testParse (t, "2013-W49T12:34-01:00", 20, 2013, 0, 49, 0, 0, 0, hm, -3600, false, utc1+hm+z );
testParse (t, "2013-W49T12:34-01", 17, 2013, 0, 49, 0, 0, 0, hm, -3600, false, utc1+hm+z );
// datetime
#ifdef AMBIGUOUS
testParse (t, "20131206", 8, 2013, 12, 0, 0, 0, 6, 0, 0, false, local6 );
#endif
testParse (t, "2013W495", 8, 2013, 0, 49, 5, 0, 0, 0, 0, false, local6 );
testParse (t, "2013W49", 7, 2013, 0, 49, 0, 0, 0, 0, 0, false, local1 );
#ifdef AMBIGUOUS
testParse (t, "2013340", 7, 2013, 0, 0, 0, 340, 0, 0, 0, false, local6 );
#endif
testParse (t, "2013-12", 7, 2013, 12, 0, 0, 0, 0, 0, 0, false, local1 );
testParse (t, "20131206T123456", 15, 2013, 12, 0, 0, 0, 6, hms, 0, false, local6+hms);
// testParse (t, "20131206T12", 11, 2013, 12, 0, 0, 0, 6, h, 0, false, local6+h );
testParse (t, "2013W495T123456", 15, 2013, 0, 49, 5, 0, 0, hms, 0, false, local6+hms);
// testParse (t, "2013W495T12", 11, 2013, 0, 49, 5, 0, 0, h, 0, false, local6+h );
testParse (t, "2013W49T123456", 14, 2013, 0, 49, 0, 0, 0, hms, 0, false, local1+hms);
// testParse (t, "2013W49T12", 10, 2013, 0, 49, 0, 0, 0, h, 0, false, local1+h );
testParse (t, "2013340T123456", 14, 2013, 0, 0, 0, 340, 0, hms, 0, false, local6+hms);
// testParse (t, "2013340T12", 10, 2013, 0, 0, 0, 340, 0, h, 0, false, local6+h );
testParse (t, "2013-12T1234", 12, 2013, 12, 0, 0, 0, 0, hm, 0, false, local1+hm );
// testParse (t, "2013-12T12", 10, 2013, 12, 0, 0, 0, 0, h, 0, false, local1+h );
testParse (t, "20131206T123456Z", 16, 2013, 12, 0, 0, 0, 6, hms, 0, true, utc6+hms );
// testParse (t, "20131206T12Z", 12, 2013, 12, 0, 0, 0, 6, h, 0, true, utc6+h );
testParse (t, "2013W495T123456Z", 16, 2013, 0, 49, 5, 0, 0, hms, 0, true, utc6+hms );
// testParse (t, "2013W495T12Z", 12, 2013, 0, 49, 5, 0, 0, h, 0, true, utc6+h );
testParse (t, "2013W49T123456Z", 15, 2013, 0, 49, 0, 0, 0, hms, 0, true, utc1+hms );
// testParse (t, "2013W49T12Z", 11, 2013, 0, 49, 0, 0, 0, h, 0, true, utc1+h );
testParse (t, "2013340T123456Z", 15, 2013, 0, 0, 0, 340, 0, hms, 0, true, utc6+hms );
// testParse (t, "2013340T12Z", 11, 2013, 0, 0, 0, 340, 0, h, 0, true, utc6+h );
testParse (t, "2013-12T123456Z", 15, 2013, 12, 0, 0, 0, 0, hms, 0, true, utc1+hms );
// testParse (t, "2013-12T12Z", 11, 2013, 12, 0, 0, 0, 0, h, 0, true, utc1+h );
testParse (t, "20131206T123456+0100", 20, 2013, 12, 0, 0, 0, 6, hms, 3600, false, utc6+hms-z);
testParse (t, "20131206T123456+01", 18, 2013, 12, 0, 0, 0, 6, hms, 3600, false, utc6+hms-z);
testParse (t, "20131206T123456-0100", 20, 2013, 12, 0, 0, 0, 6, hms, -3600, false, utc6+hms+z);
testParse (t, "20131206T123456-01", 18, 2013, 12, 0, 0, 0, 6, hms, -3600, false, utc6+hms+z);
// testParse (t, "20131206T12+0100", 16, 2013, 12, 0, 0, 0, 6, h, 3600, false, utc6+h-z );
// testParse (t, "20131206T12+01", 14, 2013, 12, 0, 0, 0, 6, h, 3600, false, utc6+h-z );
// testParse (t, "20131206T12-0100", 16, 2013, 12, 0, 0, 0, 6, h, -3600, false, utc6+h+z );
// testParse (t, "20131206T12-01", 14, 2013, 12, 0, 0, 0, 6, h, -3600, false, utc6+h+z );
testParse (t, "2013W495T123456+0100", 20, 2013, 0, 49, 5, 0, 0, hms, 3600, false, utc6+hms-z);
testParse (t, "2013W495T123456+01", 18, 2013, 0, 49, 5, 0, 0, hms, 3600, false, utc6+hms-z);
testParse (t, "2013W495T123456-0100", 20, 2013, 0, 49, 5, 0, 0, hms, -3600, false, utc6+hms+z);
testParse (t, "2013W495T123456-01", 18, 2013, 0, 49, 5, 0, 0, hms, -3600, false, utc6+hms+z);
// testParse (t, "2013W495T12+0100", 16, 2013, 0, 49, 5, 0, 0, h, 3600, false, utc6+h-z );
// testParse (t, "2013W495T12+01", 14, 2013, 0, 49, 5, 0, 0, h, 3600, false, utc6+h-z );
// testParse (t, "2013W495T12-0100", 16, 2013, 0, 49, 5, 0, 0, h, -3600, false, utc6+h+z );
// testParse (t, "2013W495T12-01", 14, 2013, 0, 49, 5, 0, 0, h, -3600, false, utc6+h+z );
testParse (t, "2013W49T123456+0100", 19, 2013, 0, 49, 0, 0, 0, hms, 3600, false, utc1+hms-z);
testParse (t, "2013W49T123456+01", 17, 2013, 0, 49, 0, 0, 0, hms, 3600, false, utc1+hms-z);
testParse (t, "2013W49T123456-0100", 19, 2013, 0, 49, 0, 0, 0, hms, -3600, false, utc1+hms+z);
testParse (t, "2013W49T123456-01", 17, 2013, 0, 49, 0, 0, 0, hms, -3600, false, utc1+hms+z);
// testParse (t, "2013W49T12+0100", 15, 2013, 0, 49, 0, 0, 0, h, 3600, false, utc1+h-z );
// testParse (t, "2013W49T12+01", 13, 2013, 0, 49, 0, 0, 0, h, 3600, false, utc1+h-z );
// testParse (t, "2013W49T12-0100", 15, 2013, 0, 49, 0, 0, 0, h, -3600, false, utc1+h+z );
// testParse (t, "2013W49T12-01", 13, 2013, 0, 49, 0, 0, 0, h, -3600, false, utc1+h+z );
testParse (t, "2013340T123456+0100", 19, 2013, 0, 0, 0, 340, 0, hms, 3600, false, utc6+hms-z);
testParse (t, "2013340T123456+01", 17, 2013, 0, 0, 0, 340, 0, hms, 3600, false, utc6+hms-z);
testParse (t, "2013340T123456-0100", 19, 2013, 0, 0, 0, 340, 0, hms, -3600, false, utc6+hms+z);
testParse (t, "2013340T123456-01", 17, 2013, 0, 0, 0, 340, 0, hms, -3600, false, utc6+hms+z);
// testParse (t, "2013340T12+0100", 15, 2013, 0, 0, 0, 340, 0, h, 3600, false, utc6+h-z );
// testParse (t, "2013340T12+01", 13, 2013, 0, 0, 0, 340, 0, h, 3600, false, utc6+h-z );
// testParse (t, "2013340T12-0100", 15, 2013, 0, 0, 0, 340, 0, h, -3600, false, utc6+h+z );
// testParse (t, "2013340T12-01", 13, 2013, 0, 0, 0, 340, 0, h, -3600, false, utc6+h+z );
testParse (t, "2013-12T123456+0100", 19, 2013, 12, 0, 0, 0, 0, hms, 3600, false, utc1+hms-z);
testParse (t, "2013-12T123456+01", 17, 2013, 12, 0, 0, 0, 0, hms, 3600, false, utc1+hms-z);
testParse (t, "2013-12T123456-0100", 19, 2013, 12, 0, 0, 0, 0, hms, -3600, false, utc1+hms+z);
testParse (t, "2013-12T123456-01", 17, 2013, 12, 0, 0, 0, 0, hms, -3600, false, utc1+hms+z);
// testParse (t, "2013-12T12+0100", 15, 2013, 12, 0, 0, 0, 0, h, 3600, false, utc1+h-z );
// testParse (t, "2013-12T12+01", 13, 2013, 12, 0, 0, 0, 0, h, 3600, false, utc1+h-z );
// testParse (t, "2013-12T12-0100", 15, 2013, 12, 0, 0, 0, 0, h, -3600, false, utc1+h+z );
// testParse (t, "2013-12T12-01", 13, 2013, 12, 0, 0, 0, 0, h, -3600, false, utc1+h+z );
// TODO Test validation of individual values.
return 0;
}

View file

@ -36,7 +36,7 @@ Context context;
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (801);
UnitTest t (799);
std::vector <std::pair <std::string, Lexer::Type>> tokens;
std::string token;
@ -80,6 +80,12 @@ int main (int argc, char** argv)
t.ok (Lexer::isBoundary ('(', '('), "'(' --> '(' = isBoundary");
t.notok (Lexer::isBoundary ('r', 'd'), "'r' --> 'd' = isBoundary");
// static bool Lexer::wasQuoted (const std::string&);
t.notok (Lexer::wasQuoted (""), "'' --> !wasQuoted");
t.notok (Lexer::wasQuoted ("foo"), "'foo' --> !wasQuoted");
t.ok (Lexer::wasQuoted ("a b"), "'a b' --> wasQuoted");
t.ok (Lexer::wasQuoted ("(a)"), "'(a)' --> wasQuoted");
// Should result in no tokens.
Lexer l0 ("");
t.notok (l0.token (token, type), "'' --> no tokens");
@ -131,7 +137,7 @@ int main (int argc, char** argv)
t.is (tokens[15].first, "'€'", "tokens[15] = \\u20ac --> ''€''");
t.is (Lexer::typeName (tokens[15].second), "string", "tokens[15] = string");
// Test for ISO-8601 dates (favoring dates in ambiguous cases).
// Test for numbers that are no longer ISO-8601 dates.
Lexer l3 ("1 12 123 1234 12345 123456 1234567 12345678");
l3.ambiguity (true);
tokens.clear ();
@ -145,21 +151,21 @@ int main (int argc, char** argv)
t.is (tokens[0].first, "1", "tokens[0] == '1'");
t.is ((int) tokens[0].second, (int) Lexer::Type::number, "tokens[0] == Type::number");
t.is (tokens[1].first, "12", "tokens[1] == '12'");
t.is ((int) tokens[1].second, (int) Lexer::Type::date, "tokens[1] == Type::date");
t.is ((int) tokens[1].second, (int) Lexer::Type::number, "tokens[1] == Type::date");
t.is (tokens[2].first, "123", "tokens[2] == '123'");
t.is ((int) tokens[2].second, (int) Lexer::Type::number, "tokens[2] == Type::number"); // 70
t.is (tokens[3].first, "1234", "tokens[3] == '1234'");
t.is ((int) tokens[3].second, (int) Lexer::Type::date, "tokens[3] == Type::date");
t.is ((int) tokens[3].second, (int) Lexer::Type::number, "tokens[3] == Type::date");
t.is (tokens[4].first, "12345", "tokens[4] == '12345'");
t.is ((int) tokens[4].second, (int) Lexer::Type::number, "tokens[4] == Type::number");
t.is (tokens[5].first, "123456", "tokens[5] == '123456'");
t.is ((int) tokens[5].second, (int) Lexer::Type::date, "tokens[5] == Type::date");
t.is ((int) tokens[5].second, (int) Lexer::Type::number, "tokens[5] == Type::date");
t.is (tokens[6].first, "1234567", "tokens[6] == '1234567'");
t.is ((int) tokens[6].second, (int) Lexer::Type::number, "tokens[6] == Type::number");
t.is (tokens[7].first, "12345678", "tokens[7] == '12345678'");
t.is ((int) tokens[7].second, (int) Lexer::Type::number, "tokens[7] == Type::number"); // 80
// Test for ISO-8601 dates (favoring numbers in ambiguous cases).
// Test for numbers that are no longer ISO-8601 dates.
Lexer l4 ("1 12 123 1234 12345 123456 1234567 12345678");
l4.ambiguity (false);
tokens.clear ();
@ -340,7 +346,6 @@ int main (int argc, char** argv)
// Date
{ "2015-W01", { { "2015-W01", Lexer::Type::date }, NO, NO, NO, NO }, },
{ "2015-02-17", { { "2015-02-17", Lexer::Type::date }, NO, NO, NO, NO }, },
{ "20131129T225800Z", { { "20131129T225800Z", Lexer::Type::date }, NO, NO, NO, NO }, },
{ "2013-11-29T22:58:00Z", { { "2013-11-29T22:58:00Z", Lexer::Type::date }, NO, NO, NO, NO }, },
// Duration

View file

@ -45,7 +45,7 @@ class Test1469(TestCase):
def test_implicit_search_sensitive_regex(self):
"""Implicit search, case sensitive, regex """
code, out, err = self.t(('list', 'möbel',
code, out, err = self.t(('list', '/möbel/',
'rc.search.case.sensitive=yes',
'rc.regex=on'))
self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code))
@ -54,7 +54,7 @@ class Test1469(TestCase):
def test_implicit_search_sensitive_noregex(self):
"""Implicit search, case sensitive, no regex """
code, out, err = self.t(('list', 'möbel',
code, out, err = self.t(('list', '/möbel/',
'rc.search.case.sensitive=yes',
'rc.regex=off'))
self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code))
@ -64,7 +64,7 @@ class Test1469(TestCase):
@unittest.skipIf('CYGWIN' in platform.system(), 'Skipping regex case-insensitive test for Cygwin')
def test_implicit_search_insensitive_regex(self):
"""Implicit search, case insensitive, regex """
code, out, err = self.t(('list', 'möbel',
code, out, err = self.t(('list', '/möbel/',
'rc.search.case.sensitive=no',
'rc.regex=on'))
self.assertEqual(0, code,
@ -74,7 +74,7 @@ class Test1469(TestCase):
def test_implicit_search_insensitive_noregex(self):
"""Implicit search, case insensitive, no regex """
code, out, err = self.t(('list', 'möbel',
code, out, err = self.t(('list', '/möbel/',
'rc.search.case.sensitive=no',
'rc.regex=off'))
self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code))