Code Salvage

- Integrated some code from the (soon to be obsolete) 2.0.0 branch,
  which is general in nature and will be needed.
- And the corresponding unit tests.
This commit is contained in:
Paul Beckingham 2010-09-05 08:48:27 -04:00
parent 366c59e25d
commit d012fc9717
18 changed files with 1675 additions and 40 deletions

125
src/Lisp.cpp Normal file
View file

@ -0,0 +1,125 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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 "Lisp.h"
////////////////////////////////////////////////////////////////////////////////
Lisp::Lisp ()
{
}
////////////////////////////////////////////////////////////////////////////////
Lisp::~Lisp ()
{
}
////////////////////////////////////////////////////////////////////////////////
Tree* Lisp::parse (const std::string& input)
{
Tree* root = new Tree ("root");
if (root)
{
Nibbler n (input);
parseNode (root, n);
}
return root;
}
////////////////////////////////////////////////////////////////////////////////
// Grammar
// node ::= '(' list ')'
// list ::= item [[space item] ...]
// item ::= word | node
void Lisp::parseNode (Tree* parent, Nibbler& n)
{
// Work on a stack-based copy, to allow backtracking.
Nibbler attempt (n);
if (attempt.skip ('('))
{
Tree* b = new Tree ("");
parent->addBranch (b);
parseList (b, attempt);
if (attempt.skip (')'))
{
n = attempt;
return;
}
}
throw std::string ("Error: malformed node");
}
////////////////////////////////////////////////////////////////////////////////
void Lisp::parseList (Tree* parent, Nibbler& n)
{
// Work on a stack-based copy, to allow backtracking.
Nibbler attempt (n);
parseItem (parent, attempt);
while (attempt.skip (' '))
parseItem (parent, attempt);
n = attempt;
}
////////////////////////////////////////////////////////////////////////////////
void Lisp::parseItem (Tree* parent, Nibbler& n)
{
// Work on a stack-based copy, to allow backtracking.
Nibbler attempt (n);
if (attempt.next () == '(')
parseNode (parent, attempt);
else
parseWord (parent, attempt);
n = attempt;
return;
}
////////////////////////////////////////////////////////////////////////////////
// A word is any group of non-whitespace followed by a space or ')'.
void Lisp::parseWord (Tree* parent, Nibbler& n)
{
// Work on a stack-based copy, to allow backtracking.
Nibbler attempt (n);
std::string word;
if (attempt.getUntilOneOf (" )", word))
{
parent->tag (word);
n = attempt;
return;
}
throw std::string ("Error: failed to parse word");
}
////////////////////////////////////////////////////////////////////////////////

54
src/Lisp.h Normal file
View file

@ -0,0 +1,54 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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_LISP
#define INCLUDED_LISP
#include <string>
#include "Tree.h"
#include "Nibbler.h"
class Lisp
{
public:
Lisp ();
Lisp (const Lisp&);
Lisp& operator= (const Lisp&);
~Lisp ();
Tree* parse (const std::string&);
private:
void parseNode (Tree*, Nibbler&);
void parseList (Tree*, Nibbler&);
void parseItem (Tree*, Nibbler&);
void parseWord (Tree*, Nibbler&);
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,17 +1,20 @@
bin_PROGRAMS = task
task_SHORTNAME = t
task_SOURCES = API.cpp Att.cpp Cmd.cpp Color.cpp Config.cpp Context.cpp \
Date.cpp Directory.cpp Duration.cpp File.cpp Filter.cpp \
Grid.cpp Hooks.cpp Keymap.cpp Location.cpp Nibbler.cpp \
Path.cpp Permission.cpp Record.cpp Sequence.cpp \
StringTable.cpp Subst.cpp TDB.cpp Table.cpp Task.cpp \
Taskmod.cpp Transport.cpp TransportSSH.cpp Timer.cpp \
command.cpp custom.cpp dependency.cpp \
edit.cpp export.cpp import.cpp interactive.cpp main.cpp \
recur.cpp report.cpp rules.cpp rx.cpp text.cpp util.cpp \
API.h Att.h Cmd.h Color.h Config.h Context.h Date.h \
Directory.h Duration.h File.h Filter.h Grid.h Hooks.h Keymap.h \
Location.h Nibbler.h Path.h Permission.h Record.h Sequence.h \
StringTable.h Subst.h TDB.h Table.h Task.h Taskmod.h Timer.h \ Transport.h TransportSSH.h i18n.h main.h text.h util.h rx.h
task_SOURCES = API.cpp API.h Att.cpp Att.h Cmd.cpp Cmd.h Color.cpp Color.h \
Config.cpp Config.h Context.cpp Context.h Date.cpp Date.h \
Directory.cpp Directory.h Duration.cpp Duration.h File.cpp \
File.h Filter.cpp Filter.h Grid.cpp Grid.h Hooks.cpp Hooks.h \
Keymap.cpp Keymap.h Lisp.cpp Lisp.h Location.cpp Location.h \
Nibbler.cpp Nibbler.h Path.cpp Path.h Permission.cpp \
Permission.h Record.cpp Record.h Rectangle.cpp Rectangle.h \
Sensor.cpp Sensor.h Sequence.cpp Sequence.h StringTable.cpp \
StringTable.h Subst.cpp Subst.h TDB.cpp TDB.h Table.cpp Table.h \
Task.cpp Task.h Taskmod.cpp Taskmod.h Thread.cpp Thread.h \
Timer.cpp Timer.h Transport.cpp Transport.h TransportSSH.cpp \
TransportSSH.h Tree.cpp Tree.h command.cpp custom.cpp \
dependency.cpp edit.cpp export.cpp i18n.h import.cpp \
interactive.cpp main.cpp main.h recur.cpp report.cpp rules.cpp \
rx.cpp rx.h text.cpp text.h util.cpp util.h
task_CPPFLAGS=$(LUA_CFLAGS)
task_LDFLAGS=$(LUA_LFLAGS)

126
src/Rectangle.cpp Normal file
View file

@ -0,0 +1,126 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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 "Rectangle.h"
////////////////////////////////////////////////////////////////////////////////
Rectangle::Rectangle ()
: left (0)
, top (0)
, width (0)
, height (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Rectangle::Rectangle (int l, int t, int w, int h)
{
left = l;
top = t;
width = w;
height = h;
}
////////////////////////////////////////////////////////////////////////////////
Rectangle::Rectangle (const Rectangle& other)
{
*this = other;
}
////////////////////////////////////////////////////////////////////////////////
Rectangle& Rectangle::operator= (const Rectangle& other)
{
if (this != &other)
{
left = other.left;
top = other.top;
width = other.width;
height = other.height;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool Rectangle::operator== (const Rectangle& other) const
{
if (left == other.left &&
top == other.top &&
width == other.width &&
height == other.height)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Rectangle::operator!= (const Rectangle& other) const
{
return !(*this == other);
}
////////////////////////////////////////////////////////////////////////////////
// Algorithm:
// Find the conditions at which the rectangles do not intersect and then
// negate the result.
bool Rectangle::intersects (const Rectangle& other) const
{
return ! (other.left > left + width - 1 ||
other.left + other.width - 1 < left ||
other.top > top + height - 1 ||
other.top + other.height - 1 < top);
}
///////////////////////////////////////////////////////////////////////////////
bool Rectangle::contains (const Rectangle& other) const
{
if (other.left >= left &&
other.left + other.width <= left + width &&
other.top >= top &&
other.top + other.height <= top + height)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Adjacent rectangles must not intersect, but must have at top/bottom,
// bottom/top, left/right or right/left adjacency.
bool Rectangle::adjacentTo (const Rectangle& other) const
{
if (! this->intersects (other))
if (top + height == other.top ||
top == other.top + other.height ||
left + width == other.left ||
left == other.left + other.width)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////

54
src/Rectangle.h Normal file
View file

@ -0,0 +1,54 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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_RECTANGLE
#define INCLUDED_RECTANGLE
class Rectangle
{
public:
Rectangle ();
Rectangle (int, int, int, int);
Rectangle (const Rectangle&);
Rectangle& operator= (const Rectangle&);
bool operator== (const Rectangle&) const;
bool operator!= (const Rectangle&) const;
bool intersects (const Rectangle&) const;
bool contains (const Rectangle&) const;
bool adjacentTo (const Rectangle&) const;
public:
int left;
int top;
int width;
int height;
};
#endif
////////////////////////////////////////////////////////////////////////////////

93
src/Sensor.cpp Normal file
View file

@ -0,0 +1,93 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2010, 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 <sys/stat.h>
#include "Sensor.h"
////////////////////////////////////////////////////////////////////////////////
Sensor::Sensor ()
: _changed (false)
, _file ("")
, _was (0)
, _is (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Sensor::~Sensor ()
{
}
////////////////////////////////////////////////////////////////////////////////
bool Sensor::changed ()
{
_is = getModification ();
if (_is != _was)
_changed = true;
return _changed;
}
////////////////////////////////////////////////////////////////////////////////
void Sensor::reset ()
{
_changed = false;
_was = _is;
}
////////////////////////////////////////////////////////////////////////////////
/*
void Sensor::fileCreation (const std::string&)
{
}
*/
////////////////////////////////////////////////////////////////////////////////
void Sensor::fileModification (const std::string& file)
{
_file = file;
_was = getModification ();
}
////////////////////////////////////////////////////////////////////////////////
/*
void Sensor::fileDeletion (const std::string&)
{
}
*/
////////////////////////////////////////////////////////////////////////////////
time_t Sensor::getModification ()
{
struct stat s = {0};
if (0 == stat (_file.c_str (), &s))
return s.st_mtime;
return 0;
}
////////////////////////////////////////////////////////////////////////////////

59
src/Sensor.h Normal file
View file

@ -0,0 +1,59 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2010, 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_SENSOR
#define INCLUDED_SENSOR
#include <string>
class Sensor
{
public:
Sensor ();
~Sensor ();
Sensor (const Sensor&);
Sensor& operator= (const Sensor&);
bool changed ();
void reset ();
// void fileCreation (const std::string&);
void fileModification (const std::string&);
// void fileDeletion (const std::string&);
private:
time_t getModification ();
private:
bool _changed;
std::string _file;
time_t _was;
time_t _is;
};
#endif
////////////////////////////////////////////////////////////////////////////////

79
src/Thread.cpp Normal file
View file

@ -0,0 +1,79 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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 <Thread.h>
////////////////////////////////////////////////////////////////////////////////
Thread::Thread ()
{
}
////////////////////////////////////////////////////////////////////////////////
Thread::~Thread ()
{
}
////////////////////////////////////////////////////////////////////////////////
int Thread::start (void* inArg)
{
mArg = inArg;
return pthread_create (&mTID, NULL, (void*(*)(void*)) Thread::entryPoint, (void*) this);
}
////////////////////////////////////////////////////////////////////////////////
void Thread::wait ()
{
pthread_join (mTID, NULL);
}
////////////////////////////////////////////////////////////////////////////////
void Thread::cancel ()
{
pthread_cancel (mTID);
}
////////////////////////////////////////////////////////////////////////////////
void Thread::detach ()
{
pthread_detach (mTID);
}
////////////////////////////////////////////////////////////////////////////////
void* Thread::entryPoint (void* inThis)
{
Thread* p = (Thread*) inThis;
p->execute (p->arg ());
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
void* Thread::arg ()
{
return mArg;
}
////////////////////////////////////////////////////////////////////////////////

54
src/Thread.h Normal file
View file

@ -0,0 +1,54 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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_THREAD
#define INCLUDED_THREAD
#include <pthread.h>
class Thread
{
public:
Thread ();
virtual ~Thread ();
int start (void* arg);
void wait ();
void cancel ();
void detach ();
void* arg ();
protected:
static void* entryPoint (void*);
virtual void execute (void*) = 0;
private:
pthread_t mTID;
void* mArg;
};
#endif
////////////////////////////////////////////////////////////////////////////////

332
src/Tree.cpp Normal file
View file

@ -0,0 +1,332 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2010, 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 <iostream>
#include <sstream>
#include "text.h"
#include "Tree.h"
////////////////////////////////////////////////////////////////////////////////
// - Tree, Branch and Node are synonymous.
// - A Tree may contain any number of branches.
// - A Branch may contain any number of name/value pairs, unique by name.
// - The destructor will delete all branches recursively.
// - Tree::enumerate is a snapshot, and is invalidated by modification.
// - Branch sequence is preserved.
Tree::Tree (const std::string& name)
: _trunk (NULL)
, _name (name)
{
}
////////////////////////////////////////////////////////////////////////////////
Tree::~Tree ()
{
for (std::vector <Tree*>::iterator i = _branches.begin ();
i != _branches.end ();
++i)
delete *i;
}
////////////////////////////////////////////////////////////////////////////////
Tree::Tree (const Tree& other)
{
throw "Unimplemented Tree::Tree (Tree&)";
}
////////////////////////////////////////////////////////////////////////////////
Tree& Tree::operator= (const Tree& other)
{
throw "Unimplemented Tree::operator= ()";
return *this;
}
////////////////////////////////////////////////////////////////////////////////
Tree* Tree::operator[] (const int branch)
{
if (branch < 0 ||
branch > (int) _branches.size () - 1)
throw "Tree::operator[] out of range";
return _branches[branch];
}
////////////////////////////////////////////////////////////////////////////////
void Tree::addBranch (Tree* branch)
{
branch->_trunk = this;
_branches.push_back (branch);
}
////////////////////////////////////////////////////////////////////////////////
void Tree::removeBranch (Tree* branch)
{
for (std::vector <Tree*>::iterator i = _branches.begin ();
i != _branches.end ();
++i)
{
if (*i == branch)
{
_branches.erase (i);
return;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Tree::replaceBranch (Tree* from, Tree* to)
{
for (unsigned int i = 0; i < _branches.size (); ++i)
{
if (_branches[i] == from)
{
to->_trunk = this;
_branches[i] = to;
return;
}
}
}
////////////////////////////////////////////////////////////////////////////////
int Tree::branches ()
{
return _branches.size ();
}
////////////////////////////////////////////////////////////////////////////////
std::string Tree::name () const
{
return _name;
}
////////////////////////////////////////////////////////////////////////////////
// Accessor for attributes.
void Tree::attribute (const std::string& name, const std::string& value)
{
_attributes[name] = value;
}
////////////////////////////////////////////////////////////////////////////////
// Accessor for attributes.
void Tree::attribute (const std::string& name, const int value)
{
std::stringstream s;
s << value;
_attributes[name] = s.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Accessor for attributes.
std::string Tree::attribute (const std::string& name)
{
// Prevent autovivification.
std::map<std::string, std::string>::iterator i = _attributes.find (name);
if (i != _attributes.end ())
return i->second;
return "";
}
////////////////////////////////////////////////////////////////////////////////
void Tree::removeAttribute (const std::string& name)
{
_attributes.erase (name);
}
////////////////////////////////////////////////////////////////////////////////
int Tree::attributes () const
{
return _attributes.size ();
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Tree::allAttributes () const
{
std::vector <std::string> names;
std::map <std::string, std::string>::const_iterator it;
for (it = _attributes.begin (); it != _attributes.end (); ++it)
names.push_back (it->first);
return names;
}
////////////////////////////////////////////////////////////////////////////////
// Recursively completes a list of Tree* objects, left to right, depth first.
// The reason for the depth-first enumeration is that a client may wish to
// traverse the tree and delete nodes. With a depth-first iteration, this is a
// safe mechanism, and a node pointer will never be dereferenced after it has
// been deleted.
void Tree::enumerate (std::vector <Tree*>& all) const
{
for (std::vector <Tree*>::const_iterator i = _branches.begin ();
i != _branches.end ();
++i)
{
(*i)->enumerate (all);
all.push_back (*i);
}
}
////////////////////////////////////////////////////////////////////////////////
Tree* Tree::parent () const
{
return _trunk;
}
////////////////////////////////////////////////////////////////////////////////
bool Tree::hasTag (const std::string& tag) const
{
if (std::find (_tags.begin (), _tags.end (), tag) != _tags.end ())
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
void Tree::tag (const std::string& tag)
{
if (! hasTag (tag))
_tags.push_back (tag);
}
////////////////////////////////////////////////////////////////////////////////
int Tree::tags () const
{
return _tags.size ();
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Tree::allTags () const
{
return _tags;
}
////////////////////////////////////////////////////////////////////////////////
int Tree::count () const
{
int total = 1; // this one.
for (std::vector <Tree*>::const_iterator i = _branches.begin ();
i != _branches.end ();
++i)
{
// Recurse and count the branches.
total += (*i)->count ();
}
return total;
}
////////////////////////////////////////////////////////////////////////////////
Tree* Tree::find (const std::string& path)
{
std::vector <std::string> elements;
split (elements, path, '/');
// Must start at the trunk.
Tree* cursor = this;
std::vector <std::string>::iterator it = elements.begin ();
if (cursor->name () != *it)
return NULL;
// Perhaps the trunk is what is needed?
if (elements.size () == 1)
return this;
// Now look for the next branch.
for (++it; it != elements.end (); ++it)
{
bool found = false;
// If the cursor has a branch that matches *it, proceed.
for (int i = 0; i < cursor->branches (); ++i)
{
if ((*cursor)[i]->name () == *it)
{
cursor = (*cursor)[i];
found = true;
break;
}
}
if (!found)
return NULL;
}
return cursor;
}
////////////////////////////////////////////////////////////////////////////////
void Tree::dumpNode (Tree* t, int depth)
{
// Dump node
for (int i = 0; i < depth; ++i)
std::cout << " ";
std::cout << t << " \033[1m" << t->name () << "\033[0m";
// Dump attributes.
std::string atts;
std::vector <std::string> attributes = t->allAttributes ();
std::vector <std::string>::iterator it;
for (it = attributes.begin (); it != attributes.end (); ++it)
{
if (it != attributes.begin ())
atts += " ";
atts += *it + "='\033[33m" + t->attribute (*it) + "\033[0m'";
}
if (atts.length ())
std::cout << " " << atts;
// Dump tags.
std::string tags;
std::vector <std::string> allTags = t->allTags ();
for (it = allTags.begin (); it != allTags.end (); ++it)
tags += (tags.length () ? " " : "") + *it;
if (tags.length ())
std::cout << " \033[32m" << tags << "\033[0m";
std::cout << std::endl;
// Recurse for branches.
for (int i = 0; i < t->branches (); ++i)
dumpNode ((*t)[i], depth + 1);
}
////////////////////////////////////////////////////////////////////////////////
void Tree::dump ()
{
std::cout << "Tree (" << count () << " nodes)" << std::endl;
dumpNode (this, 1);
}
////////////////////////////////////////////////////////////////////////////////

85
src/Tree.h Normal file
View file

@ -0,0 +1,85 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2010, 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_TREE
#define INCLUDED_TREE
#include <map>
#include <vector>
#include <string>
class Tree;
class Tree
{
public:
Tree (const std::string&);
~Tree ();
Tree (const Tree&);
Tree& operator= (const Tree&);
Tree* operator[] (const int);
void addBranch (Tree*);
void removeBranch (Tree*);
void replaceBranch (Tree*, Tree*);
int branches ();
std::string name () const;
void attribute (const std::string&, const std::string&);
void attribute (const std::string&, const int);
std::string attribute (const std::string&);
void removeAttribute (const std::string&);
int attributes () const;
std::vector <std::string> allAttributes () const;
bool hasTag (const std::string&) const;
void tag (const std::string&);
int tags () const;
std::vector <std::string> allTags () const;
void enumerate (std::vector <Tree*>& all) const;
Tree* parent () const;
int count () const;
Tree* find (const std::string&);
void dump ();
private:
void dumpNode (Tree*, int);
private:
Tree* _trunk; // Parent.
std::string _name; // Name.
std::vector <Tree*> _branches; // Children.
std::map <std::string, std::string> _attributes; // Attributes (name->value).
std::vector <std::string> _tags; // Tags (tag, tag ...).
};
#endif
////////////////////////////////////////////////////////////////////////////////

55
src/tests/.gitignore vendored
View file

@ -1,26 +1,31 @@
t.t
t.benchmark.t
tdb.t
date.t
duration.t
text.t
autocomplete.t
seq.t
att.t
record.t
stringtable.t
nibbler.t
subst.t
filt.t
cmd.t
config.t
util.t
color.t
list.t
path.t
file.t
directory.t
grid.t
rx.t
taskmod.t
*.log
att.t
autocomplete.t
cmd.t
color.t
config.t
date.t
directory.t
duration.t
file.t
filt.t
grid.t
lisp.t
list.t
nibbler.t
path.t
record.t
rectangle.t
rx.t
sensor.t
seq.t
stringtable.t
subst.t
t.benchmark.t
t.t
taskmod.t
tdb.t
text.t
tree.t
tree2.t
util.t

View file

@ -1,6 +1,7 @@
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
config.t seq.t att.t stringtable.t record.t nibbler.t subst.t filt.t \
cmd.t util.t color.t list.t path.t file.t directory.t grid.t rx.t taskmod.t
cmd.t util.t color.t list.t path.t file.t directory.t grid.t rx.t \
taskmod.t sensor.t rectangle.t tree.t tree2.t lisp.t
CFLAGS = -I. -I.. -I../.. -Wall -pedantic -ggdb3 -fno-rtti
LFLAGS = -L/usr/local/lib -lncurses -llua
OBJECTS = ../t-TDB.o ../t-Task.o ../t-text.o ../t-Date.o ../t-Table.o \
@ -12,7 +13,8 @@ OBJECTS = ../t-TDB.o ../t-Task.o ../t-text.o ../t-Date.o ../t-Table.o \
../t-export.o ../t-import.o ../t-edit.o ../t-Timer.o \
../t-Permission.o ../t-Path.o ../t-File.o ../t-Directory.o \
../t-Hooks.o ../t-API.o ../t-rx.o ../t-Taskmod.o ../t-dependency.o \
../t-Transport.o ../t-TransportSSH.o
../t-Transport.o ../t-TransportSSH.o ../t-Sensor.o ../t-Thread.o \
../t-Lisp.o ../t-Rectangle.o ../t-Tree.o
all: $(PROJECT)
@ -103,3 +105,18 @@ rx.t: rx.t.o $(OBJECTS) test.o
taskmod.t: taskmod.t.o $(OBJECTS) test.o
g++ taskmod.t.o $(OBJECTS) test.o $(LFLAGS) -o taskmod.t
lisp.t: lisp.t.o $(OBJECTS) test.o
g++ lisp.t.o $(OBJECTS) test.o $(LFLAGS) -o lisp.t
rectangle.t: rectangle.t.o $(OBJECTS) test.o
g++ rectangle.t.o $(OBJECTS) test.o $(LFLAGS) -o rectangle.t
sensor.t: sensor.t.o $(OBJECTS) test.o
g++ sensor.t.o $(OBJECTS) test.o $(LFLAGS) -o sensor.t
tree.t: tree.t.o $(OBJECTS) test.o
g++ tree.t.o $(OBJECTS) test.o $(LFLAGS) -o tree.t
tree2.t: tree2.t.o $(OBJECTS) test.o
g++ tree2.t.o $(OBJECTS) test.o $(LFLAGS) -o tree2.t

65
src/tests/lisp.t.cpp Normal file
View file

@ -0,0 +1,65 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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 "main.h"
#include "test.h"
#include "Context.h"
#include "Lisp.h"
Context context;
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest test (6);
// (one)
// t -> "one" (no tags)
// -> no child nodes
Lisp l;
Tree* t = l.parse ("(one)");
// TODO When tegelsten/Tree is merged in, uncomment this.
t->dump ();
test.is (t->branches (), 1, "(one) -> 1 node under root");
test.is ((*t)[0]->tags (), 0, "(one) -> 0 tags");
test.is ((*t)[0]->branches (), 0, "(one) -> 0 child nodes");
delete t;
// (one two)
// t -> "one" (tag: "two")
// -> no child nodes
t = l.parse ("(one two)");
// TODO When tegelsten/Tree is merged in, uncomment this.
t->dump ();
test.is (t->branches (), 1, "(one two) -> 1 node under root");
test.is ((*t)[0]->tags (), 1, "(one) -> 1 tag");
test.is ((*t)[0]->branches (), 0, "(one two) -> 0 child nodes");
delete t;
return 0;
}

162
src/tests/rectangle.t.cpp Normal file
View file

@ -0,0 +1,162 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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 "Rectangle.h"
#include "text.h"
#include "test.h"
Context context;
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (34);
// . . . . . .
// . 0 0 0 . .
// . 0 0 0 . .
// . . . . . .
// . . . . . .
// . . . . . .
Rectangle r0 (1, 1, 3, 2);
// . . . . . .
// . . . . . .
// . . . 1 1 .
// . . . 1 1 .
// . . . . . .
// . . . . . .
Rectangle r1 (3, 2, 2, 2);
// . . 2 . . .
// . . 2 . . .
// . . 2 . . .
// . . 2 . . .
// . . 2 . . .
// . . 2 . . .
Rectangle r2 (2, 0, 1, 6);
// . . . . . .
// . 3 . . . .
// . . . . . .
// . . . . . .
// . . . . . .
// . . . . . .
Rectangle r3 (1, 1, 1, 1);
// . . . . . .
// . 4 4 4 . .
// . 4 4 4 . .
// . . . . . .
// . . . . . .
// . . . . . .
Rectangle r4 (1, 1, 3, 2);
// 5 5 5 5 5 5
// 5 5 5 5 5 5
// 5 5 5 5 5 5
// 5 5 5 5 5 5
// 5 5 5 5 5 5
// 5 5 5 5 5 5
Rectangle r5 (0, 0, 6, 6);
// . . . . . .
// . . . . . .
// . . . . . .
// 6 6 . . . .
// . . . . . .
// . . . . . .
Rectangle r6 (0, 3, 2, 1);
// . . . . . .
// . . . . . .
// . . . . . .
// . . . . . .
// . . . . 7 7
// . . . . 7 7
Rectangle r7 (4, 4, 2, 2);
// . . . . . .
// . . . . . .
// 8 8 . . . .
// 8 8 . . . .
// . . . . . .
// . . . . . .
Rectangle r8 (0, 2, 2, 2);
t.ok (r0.intersects (r1), "r0.intersects (r1)");
t.ok (r0.intersects (r2), "r0.intersects (r2)");
t.ok (r0.intersects (r3), "r0.intersects (r3)");
t.ok (r0.intersects (r4), "r0.intersects (r4)");
t.ok (r0.intersects (r5), "r0.intersects (r5)");
t.ok (!r0.intersects (r6), "!r0.intersects (r6)");
t.ok (!r0.intersects (r7), "!r0.intersects (r7)");
t.ok (r0.intersects (r8), "r0.intersects (r8)");
t.ok (r1.intersects (r0), "r1.intersects (r0)");
t.ok (r2.intersects (r0), "r2.intersects (r0)");
t.ok (r3.intersects (r0), "r3.intersects (r0)");
t.ok (r4.intersects (r0), "r4.intersects (r0)");
t.ok (r5.intersects (r0), "r5.intersects (r0)");
t.ok (!r6.intersects (r0), "!r6.intersects (r0)");
t.ok (!r7.intersects (r0), "!r8.intersects (r0)");
t.ok (r8.intersects (r0), "r8.intersects (r0)");
// 2:0,0,4,12 does not overlap 1:0,10,12,4
Rectangle rBug1 (0, 0, 4, 12);
Rectangle rBug2 (0, 10, 12, 4);
t.ok (rBug1.intersects (rBug2), "rBug1.intersects (rBug2)");
t.ok (rBug2.intersects (rBug1), "rBug2.intersects (rBug1)");
t.ok (r5.contains (r0), "r5.contains (r0)");
t.ok (r5.contains (r1), "r5.contains (r1)");
t.ok (r5.contains (r2), "r5.contains (r2)");
t.ok (r5.contains (r3), "r5.contains (r3)");
t.ok (r5.contains (r4), "r5.contains (r4)");
t.ok (r5.contains (r6), "r5.contains (r6)");
t.ok (r5.contains (r7), "r5.contains (r7)");
t.ok (r5.contains (r8), "r5.contains (r8)");
t.ok (r0.contains (r3), "r0.contains (r3)");
t.ok (!r0.contains (r5), "!r0.contains (r5)");
t.ok (r0 == r4, "r0 == r4");
t.ok (r0 != r1, "r0 != r1");
Rectangle rX = r0;
t.ok (rX == r0, "rX == r0");
Rectangle rY (r0);
t.ok (rY == r0, "rY == r0");
t.notok (r0.adjacentTo (r1), "r0 not adjacent to r1");
t.ok (r1.adjacentTo (r2), "r1 is adjacent to r2");
return 0;
}
////////////////////////////////////////////////////////////////////////////////

84
src/tests/sensor.t.cpp Normal file
View file

@ -0,0 +1,84 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2010, Paul Beckingham, Federico Hernandez, 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 <fstream>
#include <unistd.h>
#include "Context.h"
#include "Sensor.h"
#include "test.h"
Context context;
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest ut (7);
// Make sure there is no file.
unlink ("./sensor.foo");
// Create sensor for missing file.
Sensor s;
s.fileModification ("./sensor.foo");
ut.ok (!s.changed (), "file not yet changed");
// Create the file.
std::ofstream one ("./sensor.foo", std::ios_base::out | std::ios_base::app);
if (one.good ())
{
one << "touch" << std::endl;
one.close ();
}
// Should register the change, so reset.
ut.ok (s.changed (), "file changed");
s.reset ();
ut.ok (!s.changed (), "file not yet changed");
// Wait a little, then modify the file.
ut.diag ("sleep 2");
sleep (2);
std::ofstream two ("./sensor.foo", std::ios_base::out | std::ios_base::app);
if (two.good ())
{
two << "touch" << std::endl;
two.close ();
}
ut.ok (s.changed (), "file changed");
ut.ok (s.changed (), "file still changed");
s.reset ();
ut.ok (!s.changed (), "file not changed again");
unlink ("./sensor.foo");
ut.ok (s.changed (), "file changed");
return 0;
}
////////////////////////////////////////////////////////////////////////////////

82
src/tests/tree.t.cpp Normal file
View file

@ -0,0 +1,82 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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 "Tree.h"
#include "test.h"
Context context;
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest ut (8);
// Construct tree as shown above.
Tree t ("");
Tree* b = new Tree ("");
b->attribute ("name", "c1");
b->tag ("tag");
t.addBranch (b);
b = new Tree ("");
b->attribute ("name", "c2");
t.addBranch (b);
b = new Tree ("");
b->attribute ("name", "c3");
t.addBranch (b);
Tree* l = new Tree ("");
l->attribute ("name", "c4");
b->addBranch (l);
// Iterate over tree.
std::vector <Tree*> all;
t.enumerate (all);
ut.is (all[0]->attribute ("name"), "c1", "c1");
ut.is (all[1]->attribute ("name"), "c2", "c2");
ut.is (all[2]->attribute ("name"), "c4", "c4");
ut.is (all[3]->attribute ("name"), "c3", "c3");
all[3]->tag ("one");
all[3]->tag ("two");
ut.ok (all[3]->hasTag ("one"), "hasTag +");
ut.notok (all[3]->hasTag ("three"), "hasTag -");
ut.is (t.count (), 5, "t.count");
all.clear ();
b->enumerate (all);
ut.is (all[0]->attribute ("name"), "c4", "t -> c3 -> c4");
return 0;
}
////////////////////////////////////////////////////////////////////////////////

156
src/tests/tree2.t.cpp Normal file
View file

@ -0,0 +1,156 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, 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 "Tree.h"
#include "test.h"
Context context;
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest ut (30);
// Create the following tree:
// t
// |
// +---+---+-+-+---+---+
// | | | | | |
// a b c d e f
// Create a tree.
Tree t ("");
// Create six branches.
Tree* a = new Tree (""); a->attribute ("name", "a");
Tree* b = new Tree (""); b->attribute ("name", "b");
Tree* c = new Tree (""); c->attribute ("name", "c");
Tree* d = new Tree (""); d->attribute ("name", "d");
Tree* e = new Tree (""); e->attribute ("name", "e");
Tree* f = new Tree (""); f->attribute ("name", "f");
// Create two branches.
Tree* x = new Tree (""); x->attribute ("name", "x");
Tree* y = new Tree (""); y->attribute ("name", "y");
// Add the six.
t.addBranch (a);
t.addBranch (b);
t.addBranch (c);
t.addBranch (d);
t.addBranch (e);
t.addBranch (f);
// Verify tree structure.
ut.ok (a->parent () == &t, "a -> t");
ut.ok (b->parent () == &t, "b -> t");
ut.ok (c->parent () == &t, "c -> t");
ut.ok (d->parent () == &t, "d -> t");
ut.ok (e->parent () == &t, "e -> t");
ut.ok (f->parent () == &t, "f -> t");
ut.ok (x->parent () == NULL, "x -> NULL");
ut.ok (y->parent () == NULL, "y -> NULL");
ut.ok (t.branches () == 6, "t[6]");
ut.diag ("---------------------------------------------------------");
// Modify the tree to become:
// t
// |
// +---+-+-+---+
// | | | |
// a b x f
// |
// +---+---+
// | | |
// c d e
// Make x the parent of c, d and e.
x->addBranch (c);
x->addBranch (d);
x->addBranch (e);
// Make x replace c as one of t's branches.
t.replaceBranch (c, x);
t.removeBranch (d);
t.removeBranch (e);
// Verify structure.
ut.ok (a->parent () == &t, "a -> t");
ut.ok (b->parent () == &t, "b -> t");
ut.ok (c->parent () == x, "c -> x");
ut.ok (d->parent () == x, "d -> x");
ut.ok (e->parent () == x, "e -> x");
ut.ok (f->parent () == &t, "f -> t");
ut.ok (x->parent () == &t, "x -> t");
ut.ok (y->parent () == NULL, "y -> NULL");
ut.ok (t.branches () == 4, "t[4]");
ut.ok (x->branches () == 3, "x[3]");
ut.diag ("---------------------------------------------------------");
// Modify the tree to become:
// t
// |
// +---+---+
// | | |
// a y f
// |
// +-+-+
// | |
// b x
// |
// +---+---+
// | | |
// c d e
// Now insert y to be parent of b, x.
y->addBranch (b);
y->addBranch (x);
t.replaceBranch (x, y);
t.removeBranch (b);
ut.ok (a->parent () == &t, "a -> t");
ut.ok (b->parent () == y, "b -> y");
ut.ok (c->parent () == x, "c -> x");
ut.ok (d->parent () == x, "d -> x");
ut.ok (e->parent () == x, "e -> x");
ut.ok (f->parent () == &t, "f -> t");
ut.ok (x->parent () == y, "x -> y");
ut.ok (y->parent () == &t, "y -> t");
ut.ok (t.branches () == 3, "t[3]");
ut.ok (x->branches () == 3, "x[3]");
ut.ok (y->branches () == 2, "y[2]");
return 0;
}
////////////////////////////////////////////////////////////////////////////////