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
API.cpp API.h
Arg.cpp Arg.h
Att.cpp Att.h
Color.cpp Color.h
Config.cpp Config.h
Context.cpp Context.h
@ -20,12 +19,10 @@ set (task_SRCS A3.cpp A3.h
File.cpp File.h
Hooks.cpp Hooks.h
JSON.cpp JSON.h
Location.cpp Location.h
Nibbler.cpp Nibbler.h
Path.cpp Path.h
Permission.cpp Permission.h
RX.cpp RX.h
TDB.cpp TDB.h
TDB2.cpp TDB2.h
Task.cpp Task.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
*.log
arguments.t
att.t
autocomplete.t
cmd.t
color.t
@ -27,7 +26,6 @@ subst.t
t.t
t2.t
taskmod.t
tdb.t
tdb2.t
text.t
transport.t

View file

@ -6,7 +6,7 @@ include_directories (${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/test
${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
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 <Context.h>
#include <Att.h>
#include <Task.h>
#include <test.h>