Code Cleanup

- Removed obsolete TDB, Att and Location code.
- Removed associated unit tests, unfortunately all ones that pass.
This commit is contained in:
Paul Beckingham 2011-09-03 13:17:03 -04:00
parent 6b3dfd0891
commit f245fa808c
11 changed files with 1 additions and 2091 deletions

View file

@ -1,701 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#include <algorithm>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <text.h>
#include <RX.h>
#include <Color.h>
#include <util.h>
#include <Date.h>
#include <Duration.h>
#include <Context.h>
#include <Att.h>
#include <main.h>
extern Context context;
static const char* internalNames[] =
{
"entry",
"start",
"end",
"parent",
"uuid",
"mask",
"imask",
"limit",
"status",
"description",
"tags",
"urgency",
// Note that annotations are not listed.
};
static const char* modifiableNames[] =
{
"project",
"priority",
"fg",
"bg",
"due",
"recur",
"until",
"wait",
"depends",
};
// Synonyms on the same line.
static const char* modifierNames[] =
{
"before", "under", "below",
"after", "over", "above",
"none",
"any",
"is", "equals",
"isnt", "not",
"has", "contains",
"hasnt",
"startswith", "left",
"endswith", "right",
"word",
"noword"
};
#define NUM_INTERNAL_NAMES (sizeof (internalNames) / sizeof (internalNames[0]))
#define NUM_MODIFIABLE_NAMES (sizeof (modifiableNames) / sizeof (modifiableNames[0]))
#define NUM_MODIFIER_NAMES (sizeof (modifierNames) / sizeof (modifierNames[0]))
////////////////////////////////////////////////////////////////////////////////
Att::Att ()
: _name ("")
, _value ("")
, _mod ("")
, _sense ("positive")
{
}
////////////////////////////////////////////////////////////////////////////////
Att::Att (const std::string& name, const std::string& mod, const std::string& value)
{
_name = name;
_value = value;
_mod = mod;
_sense = "positive";
}
////////////////////////////////////////////////////////////////////////////////
Att::Att (const std::string& name, const std::string& mod, const std::string& value,
const std::string& sense)
{
_name = name;
_value = value;
_mod = mod;
_sense = sense;
}
////////////////////////////////////////////////////////////////////////////////
Att::Att (const std::string& name, const std::string& mod, int value)
{
_name = name;
std::stringstream s;
s << value;
_value = s.str ();
_mod = mod;
_sense = "positive";
}
////////////////////////////////////////////////////////////////////////////////
Att::Att (const std::string& name, const std::string& value)
{
_name = name;
_value = value;
_mod = "";
_sense = "positive";
}
////////////////////////////////////////////////////////////////////////////////
Att::Att (const std::string& name, int value)
{
_name = name;
std::stringstream s;
s << value;
_value = s.str ();
_mod = "";
_sense = "positive";
}
////////////////////////////////////////////////////////////////////////////////
Att::Att (const Att& other)
{
_name = other._name;
_value = other._value;
_mod = other._mod;
_sense = other._sense;
}
////////////////////////////////////////////////////////////////////////////////
Att& Att::operator= (const Att& other)
{
if (this != &other)
{
_name = other._name;
_value = other._value;
_mod = other._mod;
_sense = other._sense;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool Att::operator== (const Att& other) const
{
return _name == other._name &&
_mod == other._mod &&
_value == other._value &&
_sense == other._sense;
}
////////////////////////////////////////////////////////////////////////////////
Att::~Att ()
{
}
////////////////////////////////////////////////////////////////////////////////
bool Att::logicSense (bool match) const
{
if (_sense == "positive")
return match;
else if (_sense == "negative")
return ! match;
else
{
context.debug ("_sense: " + _sense);
throw ("unknown _sense " + _sense);
}
}
////////////////////////////////////////////////////////////////////////////////
// For parsing.
bool Att::valid (const std::string& input) const
{
Nibbler n (input);
std::string ignored;
if (n.getUntilOneOf (".:", ignored))
{
if (ignored.length () == 0)
return false;
while (n.skip ('.'))
if (!n.getUntilOneOf (".:", ignored))
return false;
if (n.skip (':'))
{
if (input.find ('@') <= n.cursor () ||
input.find ('/') <= n.cursor ())
return false;
if (n.getQuoted ('"', ignored) ||
n.getUntil (' ', ignored) ||
n.getUntilEOS (ignored) ||
n.depleted ())
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Att::validInternalName (const std::string& name)
{
for (unsigned int i = 0; i < NUM_INTERNAL_NAMES; ++i)
if (name == internalNames[i])
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Att::validModifiableName (const std::string& name)
{
for (unsigned int i = 0; i < NUM_MODIFIABLE_NAMES; ++i)
if (name == modifiableNames[i])
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Att::validNameValue (
const std::string& name,
const std::string& mod,
const std::string& value)
{
std::string writableName = name;
std::string writableMod = mod;
std::string writableValue = value;
return Att::validNameValue (writableName, writableMod, writableValue);
}
////////////////////////////////////////////////////////////////////////////////
bool Att::validNameValue (
std::string& name,
std::string& mod,
std::string& value)
{
// First, guess at the full attribute name.
std::vector <std::string> candidates;
for (unsigned i = 0; i < NUM_INTERNAL_NAMES; ++i)
candidates.push_back (internalNames[i]);
for (unsigned i = 0; i < NUM_MODIFIABLE_NAMES; ++i)
candidates.push_back (modifiableNames[i]);
std::vector <std::string> matches;
autoComplete (name,
candidates,
matches,
context.config.getInteger ("abbreviation.minimum"));
if (matches.size () == 0)
return false;
else if (matches.size () != 1)
{
std::string error = "Ambiguous attribute '" + name + "' - could be either of "; // TODO i18n
std::sort (matches.begin (), matches.end ());
std::string combined;
join (combined, ", ", matches);
throw error + combined + ".";
}
name = matches[0];
// Second, guess at the modifier name.
if (mod != "")
{
candidates.clear ();
for (unsigned i = 0; i < NUM_MODIFIER_NAMES; ++i)
candidates.push_back (modifierNames[i]);
matches.clear ();
autoComplete (mod,
candidates,
matches,
context.config.getInteger ("abbreviation.minimum"));
if (matches.size () == 0)
throw std::string ("Unrecognized modifier '") + mod + "'.";
else if (matches.size () != 1)
{
std::string error = "Ambiguous modifier '" + mod + "' - could be either of "; // TODO i18n
std::sort (matches.begin (), matches.end ());
std::string combined;
join (combined, ", ", matches);
error += combined;
throw error + combined + ".";
}
mod = matches[0];
}
// Some attributes are intended to be private, unless the command is read-
// only, in which cased these are perfectly valid elements of a filter.
/*
if (context.cmd.isWriteCommand () &&
!validModifiableName (name))
throw std::string ("\"") +
name +
"\" is not an attribute you may modify directly.";
*/
else if (name == "priority")
{
if (value != "")
{
value = upperCase (value);
if (value != "H" &&
value != "M" &&
value != "L")
throw std::string ("\"") +
value +
"\" is not a valid priority. Use H, M, L or leave blank.";
}
}
else if (name == "description")
{
// if (context.cmd.isWriteCommand ())
{
if (value == "")
throw std::string ("The '") + name + "' attribute must not be blank.";
if (!noVerticalSpace (value))
throw std::string ("The '") + name + "' attribute must not contain vertical white space.";
}
}
else if (name == "fg" || name == "bg")
{
// TODO Determine whether color abbreviations are supported, and if so,
// modify 'value' here accordingly.
}
// Dates can now be either a date, or a duration that is added as an offset
// to the current date.
else if (name == "due" ||
name == "until" ||
name == "wait")
{
// Validate and convert to epoch.
if (value != "")
{
// Try parsing as a duration. If unsuccessful, try again, as a date.
try
{
Date now;
Duration dur (value);
if (dur.negative ())
value = (now - (time_t)dur).toEpochString ();
else
value = (now + (time_t)dur).toEpochString ();
}
// If the date parsing failed, try parsing as a duration. If successful,
// add the duration to the current date. If unsuccessful, propagate the
// original date parse error.
// Try parsing as a date. If unsuccessfull, throw.
catch (...)
{
try
{
value = Date (value, context.config.get ("dateformat")).toEpochString ();
}
catch (std::string& e)
{
throw e;
}
}
}
}
else if (name == "recur")
{
// Just validate, don't convert to days.
Duration d;
if (value != "")
d.parse (value);
}
else if (name == "limit")
{
if (value == "" || (value != "page" && !digitsOnly (value)))
throw std::string ("The '") + name + "' attribute must be an integer, or the value 'page'.";
}
else if (name == "status")
{
value = lowerCase (value);
std::vector <std::string> matches;
std::vector <std::string> candidates;
candidates.push_back ("pending");
candidates.push_back ("completed");
candidates.push_back ("deleted");
candidates.push_back ("recurring");
candidates.push_back ("waiting");
autoComplete (value,
candidates,
matches,
context.config.getInteger ("abbreviation.minimum"));
if (matches.size () == 1)
value = matches[0];
else
throw std::string ("\"") +
value +
"\" is not a valid status. Use 'pending', 'completed', 'deleted', 'recurring' or 'waiting'.";
}
else if (! validInternalName (name) &&
! validModifiableName (name))
throw std::string ("'") + name + "' is not a recognized attribute.";
return true;
}
////////////////////////////////////////////////////////////////////////////////
// TODO Deprecated - remove.
bool Att::validMod (const std::string& mod)
{
for (unsigned int i = 0; i < NUM_MODIFIER_NAMES; ++i)
if (modifierNames[i] == mod)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// The type of an attribute is useful for modifier evaluation.
std::string Att::type (const std::string& name) const
{
if (name == "due" ||
name == "wait" ||
name == "until" ||
name == "start" ||
name == "entry" ||
name == "end")
return "date";
else if (name == "recur")
return "duration";
else if (name == "limit" ||
name == "urgency")
return "number";
else if (name == "priority")
return "priority";
else
return "text";
}
////////////////////////////////////////////////////////////////////////////////
// ______________
// | |
// | v
// start --> name --> . --> mod --> : --> " --> value --> " --> end
// | ^ | ^
// |_____________________| |_____________|
//
void Att::parse (const std::string& input)
{
Nibbler n (input);
parse (n);
}
void Att::parse (Nibbler& n)
{
// Ensure a clean object first.
_name = "";
_value = "";
_mod = "";
_sense = "positive";
if (n.getUntilOneOf (".:", _name))
{
if (_name.length () == 0)
throw std::string ("Missing attribute name"); // TODO i18n
if (n.skip ('.'))
{
std::string mod;
if (n.skip ('~'))
{
context.debug ("using negative logic");
_sense = "negative";
}
if (n.getUntil (":", mod))
{
if (validMod (mod))
_mod = mod;
else
throw std::string ("The name '") + mod + "' is not a valid modifier."; // TODO i18n
}
else
throw std::string ("Missing . or : after modifier."); // TODO i18n
}
if (n.skip (':'))
{
// Both quoted and unquoted Att's are accepted.
// Consider removing this for a stricter parse.
if (n.getQuoted ('"', _value) ||
n.getUntilEOS (_value))
{
decode (_value);
}
}
else
throw std::string ("Missing : after attribute name."); // TODO i18n
}
else
throw std::string ("Missing : after attribute name."); // TODO i18n
/* TODO This might be too slow to include. Test this assumption.
validNameValue (_name, _mod, _value);
*/
}
////////////////////////////////////////////////////////////////////////////////
// name : " value "
std::string Att::composeF4 () const
{
std::string output = "";
if (_name != "" && _value != "")
{
std::string value = _value;
encode (value);
enquote (value);
output += _name + ":" + value;
}
return output;
}
////////////////////////////////////////////////////////////////////////////////
void Att::mod (const std::string& input)
{
if (input != "" && !validMod (input))
throw std::string ("The name '") + input + "' is not a valid modifier."; // TODO i18n
_mod = input;
}
////////////////////////////////////////////////////////////////////////////////
std::string Att::mod () const
{
return _mod;
}
////////////////////////////////////////////////////////////////////////////////
std::string Att::name () const
{
return _name;
}
////////////////////////////////////////////////////////////////////////////////
void Att::name (const std::string& name)
{
_name = name;
}
////////////////////////////////////////////////////////////////////////////////
std::string Att::value () const
{
return _value;
}
////////////////////////////////////////////////////////////////////////////////
void Att::value (const std::string& value)
{
_value = value;
}
////////////////////////////////////////////////////////////////////////////////
void Att::allNames (std::vector <std::string>& all)
{
all.clear ();
unsigned int i;
for (i = 0; i < NUM_INTERNAL_NAMES; ++i)
all.push_back (internalNames[i]);
for (i = 0; i < NUM_MODIFIABLE_NAMES; ++i)
all.push_back (modifiableNames[i]);
}
////////////////////////////////////////////////////////////////////////////////
// Add quotes.
void Att::enquote (std::string& value) const
{
value = '"' + value + '"';
}
////////////////////////////////////////////////////////////////////////////////
// Remove quotes. Instead of being picky, just remove them all. There should
// be none within the value, and this will correct for one possible corruption
// that hand-editing the pending.data file could cause.
void Att::dequote (std::string& value) const
{
str_replace (value, "\"", "");
}
////////////////////////////////////////////////////////////////////////////////
// Encode values prior to serialization.
// \t -> &tab;
// " -> &dquot;
// [ -> &open;
// ] -> &close;
// \ -> \\ (extra chars to disambiguate multi-line comment)
void Att::encode (std::string& value) const
{
str_replace (value, "\t", "&tab;");
str_replace (value, "\"", "&dquot;");
str_replace (value, "[", "&open;");
str_replace (value, "]", "&close;");
str_replace (value, "\\", "\\\\");
}
////////////////////////////////////////////////////////////////////////////////
// Decode values after parse.
// \t <- &tab;
// " <- &quot; or &dquot;
// ' <- &squot;
// , <- &comma;
// [ <- &open;
// ] <- &close;
// : <- &colon;
void Att::decode (std::string& value) const
{
// Supported encodings.
str_replace (value, "&tab;", "\t");
str_replace (value, "&dquot;", "\"");
str_replace (value, "&quot;", "'");
str_replace (value, "&open;", "[");
str_replace (value, "&close;", "]");
// Support for deprecated encodings. These cannot be removed or old files
// will not be parsable. Not just old files - completed.data can contain
// tasks formatted/encoded using these.
str_replace (value, "&squot;", "'");
str_replace (value, "&comma;", ",");
str_replace (value, "&colon;", ":");
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,87 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_ATT
#define INCLUDED_ATT
#define L10N // Localization complete.
#include <string>
#include <vector>
#include <Nibbler.h>
class Att
{
public:
Att ();
Att (const std::string&, const std::string&, const std::string&);
Att (const std::string&, const std::string&, const std::string&, const std::string&);
Att (const std::string&, const std::string&, int);
Att (const std::string&, const std::string&);
Att (const std::string&, int);
Att (const Att&);
Att& operator= (const Att&);
bool operator== (const Att&) const;
~Att ();
bool logicSense (bool match) const;
bool valid (const std::string&) const;
static bool validInternalName (const std::string&);
static bool validModifiableName (const std::string&);
static bool validNameValue (const std::string&, const std::string&, const std::string&);
static bool validNameValue (std::string&, std::string&, std::string&);
static bool validMod (const std::string&);
std::string type (const std::string&) const;
void parse (const std::string&);
void parse (Nibbler&);
std::string composeF4 () const;
void mod (const std::string&);
std::string mod () const;
std::string name () const;
void name (const std::string&);
std::string value () const;
void value (const std::string&);
static void allNames (std::vector <std::string>&);
private:
void enquote (std::string&) const;
void dequote (std::string&) const;
void encode (std::string&) const;
void decode (std::string&) const;
private:
std::string _name;
std::string _value;
std::string _mod;
std::string _sense;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -8,7 +8,6 @@ include_directories (${CMAKE_SOURCE_DIR}
set (task_SRCS A3.cpp A3.h set (task_SRCS A3.cpp A3.h
API.cpp API.h API.cpp API.h
Arg.cpp Arg.h Arg.cpp Arg.h
Att.cpp Att.h
Color.cpp Color.h Color.cpp Color.h
Config.cpp Config.h Config.cpp Config.h
Context.cpp Context.h Context.cpp Context.h
@ -20,12 +19,10 @@ set (task_SRCS A3.cpp A3.h
File.cpp File.h File.cpp File.h
Hooks.cpp Hooks.h Hooks.cpp Hooks.h
JSON.cpp JSON.h JSON.cpp JSON.h
Location.cpp Location.h
Nibbler.cpp Nibbler.h Nibbler.cpp Nibbler.h
Path.cpp Path.h Path.cpp Path.h
Permission.cpp Permission.h Permission.cpp Permission.h
RX.cpp RX.h RX.cpp RX.h
TDB.cpp TDB.h
TDB2.cpp TDB2.h TDB2.cpp TDB2.h
Task.cpp Task.h Task.cpp Task.h
Taskmod.cpp Taskmod.h Taskmod.cpp Taskmod.h

View file

@ -1,73 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#include <Location.h>
////////////////////////////////////////////////////////////////////////////////
Location::Location ()
: _path ("")
, _pending (NULL)
, _completed (NULL)
, _undo (NULL)
{
}
////////////////////////////////////////////////////////////////////////////////
Location::Location (const std::string& p)
: _path (p)
{
}
////////////////////////////////////////////////////////////////////////////////
Location::Location (const Location& other)
{
_path = other._path;
_pending = other._pending;
_completed = other._completed;
_undo = other._undo;
}
////////////////////////////////////////////////////////////////////////////////
Location& Location::operator= (const Location& other)
{
if (this != &other)
{
_path = other._path;
_pending = other._pending;
_completed = other._completed;
_undo = other._undo;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
Location::~Location ()
{
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,51 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_LOCATION
#define INCLUDED_LOCATION
#define L10N // Localization complete.
#include <string>
#include <stdio.h>
class Location
{
public:
Location (); // Default constructor
Location (const std::string&); // Default constructor
Location (const Location&); // Copy constructor
Location& operator= (const Location&); // Assignment operator
~Location (); // Destructor
public:
std::string _path;
FILE* _pending;
FILE* _completed;
FILE* _undo;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,693 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <sstream>
#include <algorithm>
#include <set>
#include <list>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/file.h>
#include <stdlib.h>
#include <text.h>
#include <util.h>
#include <TDB.h>
#include <Directory.h>
#include <File.h>
#include <ViewText.h>
#include <Color.h>
#include <main.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
// The ctor/dtor do nothing.
// The lock/unlock methods hold the file open.
// There should be only one commit.
//
// +- TDB::TDB
// |
// | +- TDB::lock
// | | open
// | | [lock]
// | |
// | | +- TDB::load
// | | | read all
// | | | return subset
// | | |
// | | +- TDB::add (T)
// | | |
// | | +- TDB::update (T)
// | | |
// | | +- TDB::commit
// | | | write all
// | | |
// | | +- TDB::undo
// | |
// | +- TDB::unlock
// | [unlock]
// | close
// |
// +- TDB::~TDB
// [TDB::unlock]
//
TDB::TDB ()
: _lock (true)
, _all_opened_and_locked (false)
, _id (1)
{
}
////////////////////////////////////////////////////////////////////////////////
TDB::~TDB ()
{
if (_all_opened_and_locked)
unlock ();
}
////////////////////////////////////////////////////////////////////////////////
void TDB::clear ()
{
_locations.clear ();
_lock = true;
if (_all_opened_and_locked)
unlock ();
_all_opened_and_locked = false;
_id = 1;
_pending.clear ();
_new.clear ();
_completed.clear ();
_modified.clear ();
}
////////////////////////////////////////////////////////////////////////////////
void TDB::location (const std::string& path)
{
Directory d (path);
if (!d.exists ())
throw std::string ("Data location '") +
path +
"' does not exist, or is not readable and writable.";
_locations.push_back (Location (d));
}
////////////////////////////////////////////////////////////////////////////////
void TDB::lock (bool lockFile /* = true */)
{
_lock = lockFile;
_pending.clear ();
_new.clear ();
_completed.clear ();
_modified.clear ();
foreach (location, _locations)
{
location->_pending = openAndLock (location->_path + "/pending.data");
location->_completed = openAndLock (location->_path + "/completed.data");
location->_undo = openAndLock (location->_path + "/undo.data");
}
_all_opened_and_locked = true;
}
////////////////////////////////////////////////////////////////////////////////
void TDB::unlock ()
{
// Do not clear out these items, as they may be used in a read-only fashion.
// _pending.clear ();
// _new.clear ();
// _modified.clear ();
foreach (location, _locations)
{
fflush (location->_pending);
fclose (location->_pending);
location->_pending = NULL;
fflush (location->_completed);
fclose (location->_completed);
location->_completed = NULL;
fflush (location->_undo);
fclose (location->_undo);
location->_completed = NULL;
}
_all_opened_and_locked = false;
}
////////////////////////////////////////////////////////////////////////////////
// Returns number of filtered tasks.
// Note: tasks.clear () is deliberately not called, to allow the combination of
// multiple files.
int TDB::load (std::vector <Task>& tasks)
{
// Special optimization: if the filter contains Att ('status', '', 'pending'),
// and no other 'status' filters, then loadCompleted can be skipped.
int numberStatusClauses = 0;
int numberSimpleStatusClauses = 0;
loadPending (tasks);
if (numberStatusClauses == 0 ||
numberStatusClauses != numberSimpleStatusClauses)
loadCompleted (tasks);
else
context.debug ("load optimization short circuit");
return tasks.size ();
}
////////////////////////////////////////////////////////////////////////////////
// Returns number of filtered tasks.
// Note: tasks.clear () is deliberately not called, to allow the combination of
// multiple files.
int TDB::loadPending (std::vector <Task>& tasks)
{
std::string file;
int line_number = 1;
try
{
// Only load if not already loaded.
if (_pending.size () == 0)
{
_id = 1;
char line[T_LINE_MAX];
foreach (location, _locations)
{
line_number = 1;
file = location->_path + "/pending.data";
fseek (location->_pending, 0, SEEK_SET);
while (fgets (line, T_LINE_MAX, location->_pending))
{
int length = strlen (line);
if (length > 3) // []\n
{
// TODO Add hidden attribute indicating source?
Task task (line);
task.id = _id++;
_pending.push_back (task);
// Maintain mapping for ease of link/dependency resolution.
// Note that this mapping is not restricted by the filter, and is
// therefore a complete set.
_I2U[task.id] = task.get ("uuid");
_U2I[task.get ("uuid")] = task.id;
}
++line_number;
}
}
}
// Now filter and return.
foreach (task, _pending)
tasks.push_back (*task);
// Hand back any accumulated additions, if TDB::loadPending is being called
// repeatedly.
int fakeId = _id;
foreach (task, _new)
{
task->id = fakeId++;
tasks.push_back (*task);
}
}
catch (std::string& e)
{
std::stringstream s;
s << " in " << file << " at line " << line_number;
throw e + s.str ();
}
return tasks.size ();
}
////////////////////////////////////////////////////////////////////////////////
// Returns number of filtered tasks.
// Note: tasks.clear () is deliberately not called, to allow the combination of
// multiple files.
int TDB::loadCompleted (std::vector <Task>& tasks)
{
std::string file;
int line_number = 1;
try
{
if (_completed.size () == 0)
{
char line[T_LINE_MAX];
foreach (location, _locations)
{
line_number = 1;
file = location->_path + "/completed.data";
fseek (location->_completed, 0, SEEK_SET);
while (fgets (line, T_LINE_MAX, location->_completed))
{
int length = strlen (line);
if (length > 3) // []\n
{
// TODO Add hidden attribute indicating source?
Task task (line);
task.id = 0; // Need a value, just not a valid value.
_completed.push_back (task);
}
++line_number;
}
}
}
// Now filter and return.
foreach (task, _completed)
tasks.push_back (*task);
}
catch (std::string& e)
{
std::stringstream s;
s << " in " << file << " at line " << line_number;
throw e + s.str ();
}
return tasks.size ();
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task>& TDB::getAllPending ()
{
return _pending;
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task>& TDB::getAllNew ()
{
return _new;
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task>& TDB::getAllCompleted ()
{
return _completed;
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task>& TDB::getAllModified ()
{
return _modified;
}
////////////////////////////////////////////////////////////////////////////////
// Note: _locations[0] is where all tasks are written.
void TDB::add (const Task& task)
{
std::string unique;
Task t (task);
if (task.get ("uuid") == "")
unique = ::uuid ();
else
unique = task.get ("uuid");
t.set ("uuid", unique);
// If the tasks are loaded, then verify that this uuid is not already in
// the file.
if (uuidAlreadyUsed (unique, _new) ||
uuidAlreadyUsed (unique, _modified) ||
uuidAlreadyUsed (unique, _pending) ||
uuidAlreadyUsed (unique, _completed))
throw std::string ("Cannot add task because the uuid '") + unique + "' is not unique.";
_new.push_back (t);
_I2U[task.id] = unique;
_U2I[task.get ("uuid")] = t.id;
}
////////////////////////////////////////////////////////////////////////////////
void TDB::update (const Task& task)
{
_modified.push_back (task);
}
////////////////////////////////////////////////////////////////////////////////
// Interestingly, only the pending file gets written to. The completed file is
// only modified by TDB::gc.
int TDB::commit ()
{
context.timer_gc.start ();
int quantity = _new.size () + _modified.size ();
// This is an optimization. If there are only new tasks, and none were
// modified, simply seek to the end of pending and write.
if (_new.size () && ! _modified.size ())
{
fseek (_locations[0]._pending, 0, SEEK_END);
foreach (task, _new)
{
_pending.push_back (*task);
fputs (task->composeF4 ().c_str (), _locations[0]._pending);
}
fseek (_locations[0]._undo, 0, SEEK_END);
foreach (task, _new)
writeUndo (*task, _locations[0]._undo);
_new.clear ();
return quantity;
}
// The alternative is to potentially rewrite both files.
else if (_new.size () || _modified.size ())
{
// allPending is a copy of _pending, with all modifications included, and
// new tasks appended.
std::vector <Task> allPending;
allPending = _pending;
foreach (mtask, _modified)
{
foreach (task, allPending)
{
if (task->id == mtask->id)
{
*task = *mtask;
goto next_mod;
}
}
next_mod:
;
}
foreach (task, _new)
allPending.push_back (*task);
// Write out all pending.
if (fseek (_locations[0]._pending, 0, SEEK_SET) == 0)
{
if (ftruncate (fileno (_locations[0]._pending), 0))
throw std::string ("Failed to truncate pending.data file ");
foreach (task, allPending)
fputs (task->composeF4 ().c_str (), _locations[0]._pending);
}
// Update the undo log.
if (fseek (_locations[0]._undo, 0, SEEK_END) == 0)
{
foreach (mtask, _modified)
{
foreach (task, _pending)
{
if (task->id == mtask->id)
{
writeUndo (*task, *mtask, _locations[0]._undo);
goto next_mod2;
}
}
next_mod2:
;
}
foreach (task, _new)
writeUndo (*task, _locations[0]._undo);
}
_pending = allPending;
_modified.clear ();
_new.clear ();
}
context.timer_gc.stop ();
return quantity;
}
////////////////////////////////////////////////////////////////////////////////
// Scans the pending tasks for any that are completed or deleted, and if so,
// moves them to the completed.data file. Returns a count of tasks moved.
// Now reverts expired waiting tasks to pending.
// Now cleans up dangling dependencies.
int TDB::gc ()
{
context.timer_gc.start ();
// Allowed as a temporary override.
if (!context.config.getBoolean ("gc"))
{
context.timer_gc.stop ();
return 0;
}
int count_pending_changes = 0;
int count_completed_changes = 0;
Date now;
if (_new.size ())
throw std::string ("Unexpected new tasks found during gc.");
if (_modified.size ())
throw std::string ("Unexpected modified tasks found during gc.");
lock ();
std::vector <Task> ignore;
loadPending (ignore);
// Search for dangling dependencies. These are dependencies whose uuid cannot
// be converted to an id by TDB.
std::vector <std::string> deps;
foreach (task, _pending)
{
if (task->has ("depends"))
{
deps.clear ();
task->getDependencies (deps);
foreach (dep, deps)
{
if (id (*dep) == 0)
{
task->removeDependency (*dep);
++count_pending_changes;
context.debug ("GC: Removed dangling dependency "
+ task->get ("uuid")
+ " -> "
+ *dep);
}
}
}
}
// Now move completed and deleted tasks from the pending list to the
// completed list. Isn't garbage collection easy?
std::vector <Task> still_pending;
std::vector <Task> newly_completed;
foreach (task, _pending)
{
Task::status s = task->getStatus ();
if (s == Task::completed ||
s == Task::deleted)
{
newly_completed.push_back (*task);
++count_pending_changes; // removal
++count_completed_changes; // addition
}
else if (s == Task::waiting)
{
// Wake up tasks that need to be woken.
Date wait_date (task->get_date ("wait"));
if (now > wait_date)
{
task->setStatus (Task::pending);
task->remove ("wait");
++count_pending_changes; // modification
context.debug (std::string ("TDB::gc waiting -> pending for ") +
task->get ("uuid"));
}
still_pending.push_back (*task);
}
else
still_pending.push_back (*task);
}
// No commit - all updates performed manually.
if (count_pending_changes > 0)
{
if (fseek (_locations[0]._pending, 0, SEEK_SET) == 0)
{
if (ftruncate (fileno (_locations[0]._pending), 0))
throw std::string ("Failed to truncate pending.data file ");
foreach (task, still_pending)
fputs (task->composeF4 ().c_str (), _locations[0]._pending);
// Update cached copy.
_pending = still_pending;
}
}
// Append the new_completed tasks to completed.data. No need to write out the
// whole list.
if (count_completed_changes > 0)
{
fseek (_locations[0]._completed, 0, SEEK_END);
foreach (task, newly_completed)
fputs (task->composeF4 ().c_str (), _locations[0]._completed);
}
// Close files.
unlock ();
std::stringstream s;
s << "gc " << (count_pending_changes + count_completed_changes) << " tasks";
context.debug (s.str ());
context.timer_gc.stop ();
return count_pending_changes + count_completed_changes;
}
////////////////////////////////////////////////////////////////////////////////
int TDB::nextId ()
{
return _id++;
}
////////////////////////////////////////////////////////////////////////////////
void TDB::undo ()
{
}
////////////////////////////////////////////////////////////////////////////////
void TDB::merge (const std::string& mergeFile)
{
}
////////////////////////////////////////////////////////////////////////////////
std::string TDB::uuid (int id) const
{
std::map <int, std::string>::const_iterator i;
if ((i = _I2U.find (id)) != _I2U.end ())
return i->second;
return "";
}
////////////////////////////////////////////////////////////////////////////////
int TDB::id (const std::string& uuid) const
{
std::map <std::string, int>::const_iterator i;
if ((i = _U2I.find (uuid)) != _U2I.end ())
return i->second;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
FILE* TDB::openAndLock (const std::string& file)
{
// TODO Need provision here for read-only locations.
// Check for access.
File f (file);
bool exists = f.exists ();
if (exists)
if (!f.readable () || !f.writable ())
throw std::string ("Task does not have the correct permissions for '") +
file + "'.";
// Open the file.
FILE* in = fopen (file.c_str (), (exists ? "r+" : "w+"));
if (!in)
throw std::string ("Could not open '") + file + "'.";
// Lock if desired. Try three times before failing.
if (_lock)
{
int retry = 0;
while (flock (fileno (in), LOCK_NB | LOCK_EX) && ++retry <= 3)
{
std::cout << "Waiting for file lock...\n";
while (flock (fileno (in), LOCK_NB | LOCK_EX) && ++retry <= 3)
delay (0.2);
}
if (retry > 3)
throw std::string ("Could not lock '") + file + "'.";
}
return in;
}
////////////////////////////////////////////////////////////////////////////////
void TDB::writeUndo (const Task& after, FILE* file)
{
fprintf (file,
"time %u\nnew %s---\n",
(unsigned int) time (NULL),
after.composeF4 ().c_str ());
}
////////////////////////////////////////////////////////////////////////////////
void TDB::writeUndo (const Task& before, const Task& after, FILE* file)
{
fprintf (file,
"time %u\nold %snew %s---\n",
(unsigned int) time (NULL),
before.composeF4 ().c_str (),
after.composeF4 ().c_str ());
}
////////////////////////////////////////////////////////////////////////////////
bool TDB::uuidAlreadyUsed (
const std::string& uuid,
const std::vector <Task>& all)
{
std::vector <Task>::const_iterator it;
for (it = all.begin (); it != all.end (); ++it)
if (it->get ("uuid") == uuid)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,98 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_TDB
#define INCLUDED_TDB
#define L10N // Localization complete.
#include <map>
#include <vector>
#include <string>
#include <Location.h>
#include <Task.h>
// Length of longest line.
#define T_LINE_MAX 32768
class TDB
{
public:
TDB (); // Default constructor
~TDB (); // Destructor
TDB (const TDB&);
TDB& operator= (const TDB&);
void clear ();
void location (const std::string&);
void lock (bool lockFile = true);
void unlock ();
int load (std::vector <Task>&);
int loadPending (std::vector <Task>&);
int loadCompleted (std::vector <Task>&);
const std::vector <Task>& getAllPending ();
const std::vector <Task>& getAllNew ();
const std::vector <Task>& getAllCompleted ();
const std::vector <Task>& getAllModified ();
void add (const Task&); // Single task add to pending
void update (const Task&); // Single task update to pending
int commit (); // Write out all tasks
int gc (); // Clean up pending
int nextId ();
void undo ();
void merge (const std::string&);
std::string uuid (int) const;
int id (const std::string&) const;
private:
FILE* openAndLock (const std::string&);
void writeUndo (const Task&, FILE*);
void writeUndo (const Task&, const Task&, FILE*);
bool uuidAlreadyUsed (const std::string&);
bool uuidAlreadyUsed (const std::string&, const std::vector <Task>&);
private:
std::vector <Location> _locations;
bool _lock;
bool _all_opened_and_locked;
int _id;
std::vector <Task> _pending; // Contents of pending.data
std::vector <Task> _completed; // Contents of pending.data
std::vector <Task> _new; // Uncommitted new tasks
std::vector <Task> _modified; // Uncommitted modified tasks
std::map <int, std::string> _I2U; // ID -> UUID map
std::map <std::string, int> _U2I; // UUID -> ID map
};
#endif
////////////////////////////////////////////////////////////////////////////////

2
test/.gitignore vendored
View file

@ -2,7 +2,6 @@
*.data *.data
*.log *.log
arguments.t arguments.t
att.t
autocomplete.t autocomplete.t
cmd.t cmd.t
color.t color.t
@ -27,7 +26,6 @@ subst.t
t.t t.t
t2.t t2.t
taskmod.t taskmod.t
tdb.t
tdb2.t tdb2.t
text.t text.t
transport.t transport.t

View file

@ -6,7 +6,7 @@ include_directories (${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/test ${CMAKE_SOURCE_DIR}/test
${TASK_INCLUDE_DIRS}) ${TASK_INCLUDE_DIRS})
set (test_SRCS att.t autocomplete.t color.t config.t date.t directory.t dom.t set (test_SRCS autocomplete.t color.t config.t date.t directory.t dom.t
duration.t file.t i18n.t json.t list.t nibbler.t path.t rx.t duration.t file.t i18n.t json.t list.t nibbler.t path.t rx.t
t.t t2.t taskmod.t tdb2.t text.t uri.t util.t view.t json_test) t.t t2.t taskmod.t tdb2.t text.t uri.t util.t view.t json_test)

View file

@ -1,381 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#include <Context.h>
#include <Att.h>
#include <test.h>
#include <algorithm>
Context context;
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (118);
Att a;
t.notok (a.valid ("name"), "Att::valid name -> fail");
t.notok (a.valid (":"), "Att::valid : -> fail");
t.notok (a.valid (":value"), "Att::valid :value -> fail");
t.notok (a.valid ("n@me.mod:value"), "Att::valie n@me.mod:value -> fail");
t.notok (a.valid ("n/me.mod:value"), "Att::valie n/me.mod:value -> fail");
t.notok (a.valid ("name.m@d:value"), "Att::valie name.m@d:value -> fail");
t.notok (a.valid ("name.m/d:value"), "Att::valie name.m/d:value -> fail");
t.ok (a.valid ("name:value"), "Att::valid name:value");
t.ok (a.valid ("name:value "), "Att::valid name:value\\s");
t.ok (a.valid ("name:'value'"), "Att::valid name:'value'");
t.ok (a.valid ("name:'one two'"), "Att::valid name:'one two'");
t.ok (a.valid ("name:\"value\""), "Att::valid name:\"value\"");
t.ok (a.valid ("name:\"one two\""), "Att::valid name:\"one two\"");
t.ok (a.valid ("name:"), "Att::valid name:");
t.ok (a.valid ("name:&quot;"), "Att::valid &quot;");
t.ok (a.valid ("name.one:value"), "Att::valid name.one.value");
t.ok (a.valid ("name.one.two:value"), "Att::valid name.one.two:value");
t.ok (a.valid ("name.:value"), "Att::valid name.:value");
t.ok (a.valid ("name..:value"), "Att::valid name..:value");
Att a1 ("name", "value");
t.is (a1.name (), "name", "Att::Att (name, value), Att.name");
t.is (a1.value (), "value", "Att::Att (name, value), Att.value");
Att a2;
a2.name ("name");
a2.value ("value");
t.is (a2.name (), "name", "Att::Att (), Att.name");
t.is (a2.value (), "value", "Att::Att (), Att.value");
Att a3 (a2);
t.is (a3.name (), "name", "Att::Att (Att), Att.name");
t.is (a3.value (), "value", "Att::Att (Att), Att.value");
Att a4;
a4 = a2;
t.is (a4.name (), "name", "Att::Att (), Att.operator=, Att.name");
t.is (a4.value (), "value", "Att::Att (), Att.operator=, Att.value");
Att a5 ("name", "value");
t.is (a5.composeF4 (), "name:\"value\"", "Att::composeF4 simple");
a5.value ("\"");
t.is (a5.composeF4 (), "name:\"&dquot;\"", "Att::composeF4 encoded \"");
a5.value ("\t\",[]:");
t.is (a5.composeF4 (), "name:\"&tab;&dquot;,&open;&close;:\"", "Att::composeF4 fully encoded \\t\",[]:");
Att a6 ("name", 7);
// Att::mod - straight comparisons.
bool good = true;
try {a6.mod ("is");} catch (...) {good = false;}
t.ok (good, "Att::mod (is)");
good = true;
try {a6.mod ("before");} catch (...) {good = false;}
t.ok (good, "Att::mod (before)");
good = true;
try {a6.mod ("after");} catch (...) {good = false;}
t.ok (good, "Att::mod (after)");
good = true;
try {a6.mod ("none");} catch (...) {good = false;}
t.ok (good, "Att::mod (none)");
good = true;
try {a6.mod ("any");} catch (...) {good = false;}
t.ok (good, "Att::mod (any)");
good = true;
try {a6.mod ("over");} catch (...) {good = false;}
t.ok (good, "Att::mod (over)");
good = true;
try {a6.mod ("under");} catch (...) {good = false;}
t.ok (good, "Att::mod (under)");
good = true;
try {a6.mod ("above");} catch (...) {good = false;}
t.ok (good, "Att::mod (above)");
good = true;
try {a6.mod ("below");} catch (...) {good = false;}
t.ok (good, "Att::mod (below)");
good = true;
try {a6.mod ("isnt");} catch (...) {good = false;}
t.ok (good, "Att::mod (isnt)");
good = true;
try {a6.mod ("has");} catch (...) {good = false;}
t.ok (good, "Att::mod (has)");
good = true;
try {a6.mod ("contains");} catch (...) {good = false;}
t.ok (good, "Att::mod (contains)");
good = true;
try {a6.mod ("hasnt");} catch (...) {good = false;}
t.ok (good, "Att::mod (hasnt)");
good = true;
try {a6.mod ("startswith");} catch (...) {good = false;}
t.ok (good, "Att::mod (startswith)");
good = true;
try {a6.mod ("endswith");} catch (...) {good = false;}
t.ok (good, "Att::mod (endswith)");
good = true;
try {a6.mod ("word");} catch (...) {good = false;}
t.ok (good, "Att::mod (word)");
good = true;
try {a6.mod ("noword");} catch (...) {good = false;}
t.ok (good, "Att::mod (noword)");
good = true;
try {a6.mod ("unrecognized");} catch (...) {good = false;}
t.notok (good, "Att::mod (unrecognized)");
// Att::mod - regex comparisons.
context.config.set ("regex", "on");
good = true;
try {a6.mod ("is");} catch (...) {good = false;}
t.ok (good, "Att::mod (is)");
good = true;
try {a6.mod ("before");} catch (...) {good = false;}
t.ok (good, "Att::mod (before)");
good = true;
try {a6.mod ("after");} catch (...) {good = false;}
t.ok (good, "Att::mod (after)");
good = true;
try {a6.mod ("none");} catch (...) {good = false;}
t.ok (good, "Att::mod (none)");
good = true;
try {a6.mod ("any");} catch (...) {good = false;}
t.ok (good, "Att::mod (any)");
good = true;
try {a6.mod ("over");} catch (...) {good = false;}
t.ok (good, "Att::mod (over)");
good = true;
try {a6.mod ("under");} catch (...) {good = false;}
t.ok (good, "Att::mod (under)");
good = true;
try {a6.mod ("above");} catch (...) {good = false;}
t.ok (good, "Att::mod (above)");
good = true;
try {a6.mod ("below");} catch (...) {good = false;}
t.ok (good, "Att::mod (below)");
good = true;
try {a6.mod ("isnt");} catch (...) {good = false;}
t.ok (good, "Att::mod (isnt)");
good = true;
try {a6.mod ("has");} catch (...) {good = false;}
t.ok (good, "Att::mod (has)");
good = true;
try {a6.mod ("contains");} catch (...) {good = false;}
t.ok (good, "Att::mod (contains)");
good = true;
try {a6.mod ("hasnt");} catch (...) {good = false;}
t.ok (good, "Att::mod (hasnt)");
good = true;
try {a6.mod ("startswith");} catch (...) {good = false;}
t.ok (good, "Att::mod (startswith)");
good = true;
try {a6.mod ("endswith");} catch (...) {good = false;}
t.ok (good, "Att::mod (endswith)");
good = true;
try {a6.mod ("word");} catch (...) {good = false;}
t.ok (good, "Att::mod (word)");
good = true;
try {a6.mod ("noword");} catch (...) {good = false;}
t.ok (good, "Att::mod (noword)");
good = true;
try {a6.mod ("unrecognized");} catch (...) {good = false;}
t.notok (good, "Att::mod (unrecognized)");
// Att::parse
Nibbler n ("");
Att a7;
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse () -> throw");
n = Nibbler ("name:value");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"",
"Att::parse (name:value)");
n = Nibbler ("name:\"value\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"",
"Att::parse (name:\"value\")");
n = Nibbler ("name:\"one two\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"one two\"",
"Att::parse (name:\"one two\")");
n = Nibbler ("name:\"&quot;\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"'\"",
"Att::parse (name:\"'\")");
n = Nibbler ("name:\"&tab;&quot;&comma;&open;&close;&colon;\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"&tab;',&open;&close;:\"",
"Att::parse (name:\"&tab;',&open;&close;:\")");
n = Nibbler ("total gibberish");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse (total gibberish)");
n = Nibbler ("malformed");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse (malformed)");
n = Nibbler (":malformed");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse (:malformed)");
n = Nibbler (":\"\"");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse (:\"\")");
n = Nibbler (":\"");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse (:\")");
n = Nibbler ("name:");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.ok (good, "Att::parse (name:)");
n = Nibbler ("name:\"value");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.ok (good, "Att::parse (name:\"value)");
t.is (a7.composeF4 (), "name:\"&dquot;value\"", "Att::composeF4 -> name:\"&dquot;value\"");
n = Nibbler ("name:value\"");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.ok (good, "Att::parse (name:value\")");
t.is (a7.composeF4 (), "name:\"value&dquot;\"", "Att::composeF4 -> name:\"value&dquot;\"");
n = Nibbler ("name:val\"ue");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.ok (good, "Att::parse (name:val\"ue)");
t.is (a7.composeF4 (), "name:\"val&dquot;ue\"", "Att::composeF4 -> name:\"val&dquot;ue\"");
n = Nibbler ("name\"");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse (name\")");
// Mods
n = Nibbler ("name.any:\"value\"");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.ok (good, "Att::parse (name.any:\"value\")");
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 -> name:\"value\"");
n = Nibbler ("name.bogus:\"value\"");
good = true;
try {a7.parse (n);} catch (...) {good = false;}
t.notok (good, "Att::parse (name.bogus:\"value\")");
// Att::type
t.is (a.type ("entry"), "date", "Att::type entry -> date");
t.is (a.type ("due"), "date", "Att::type due -> date");
t.is (a.type ("until"), "date", "Att::type until -> date");
t.is (a.type ("start"), "date", "Att::type start -> date");
t.is (a.type ("end"), "date", "Att::type end -> date");
t.is (a.type ("wait"), "date", "Att::type wait -> date");
t.is (a.type ("recur"), "duration", "Att::type recur -> duration");
t.is (a.type ("limit"), "number", "Att::type limit -> number");
t.is (a.type ("description"), "text", "Att::type description -> text");
t.is (a.type ("foo"), "text", "Att::type foo -> text");
// Att::validInternalName
t.ok (Att::validInternalName ("entry"), "internal entry");
t.ok (Att::validInternalName ("start"), "internal start");
t.ok (Att::validInternalName ("end"), "internal end");
t.ok (Att::validInternalName ("parent"), "internal parent");
t.ok (Att::validInternalName ("uuid"), "internal uuid");
t.ok (Att::validInternalName ("mask"), "internal mask");
t.ok (Att::validInternalName ("imask"), "internal imask");
t.ok (Att::validInternalName ("limit"), "internal limit");
t.ok (Att::validInternalName ("status"), "internal status");
t.ok (Att::validInternalName ("description"), "internal description");
// Att::validModifiableName
t.ok (Att::validModifiableName ("project"), "modifiable project");
t.ok (Att::validModifiableName ("priority"), "modifiable priority");
t.ok (Att::validModifiableName ("fg"), "modifiable fg");
t.ok (Att::validModifiableName ("bg"), "modifiable bg");
t.ok (Att::validModifiableName ("due"), "modifiable due");
t.ok (Att::validModifiableName ("recur"), "modifiable recur");
t.ok (Att::validModifiableName ("until"), "modifiable until");
t.ok (Att::validModifiableName ("wait"), "modifiable wait");
// Att::allNames
std::vector <std::string> all;
Att::allNames (all);
std::vector <std::string>::iterator it;
it = std::find (all.begin (), all.end (), "uuid");
t.ok (it != all.end (), "internal name 'uuid' found in Att::allNames");
it = std::find (all.begin (), all.end (), "project");
t.ok (it != all.end (), "modifiable name 'project' found in Att::allNames");
return 0;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -26,7 +26,6 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <iostream> #include <iostream>
#include <Context.h> #include <Context.h>
#include <Att.h>
#include <Task.h> #include <Task.h>
#include <test.h> #include <test.h>