From f3724aa7147f30928c335dc59fccc5f36c431ffe Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 23 May 2009 16:10:42 -0400 Subject: [PATCH] Enhancement - Sequence object - Implemented sequence object to encapsulate the worries of parsing sequences. - Implemented unit tests. --- src/Makefile.am | 2 +- src/{objects => }/Sequence.cpp | 76 +++++++++++++++++---- src/{objects => }/Sequence.h | 6 +- src/task.h | 2 + src/tests/.gitignore | 1 + src/tests/Makefile | 7 +- src/tests/autocomplete.t.cpp | 3 +- src/tests/duration.t.cpp | 6 +- src/tests/parse.t.cpp | 1 + src/tests/seq.t.cpp | 118 +++++++++++++++++++++++++++++++++ src/tests/t.benchmark.t.cpp | 4 +- src/tests/t.t.cpp | 5 +- src/tests/tdb.t.cpp | 4 +- src/tests/test.cpp | 2 + src/tests/text.t.cpp | 1 + 15 files changed, 209 insertions(+), 29 deletions(-) rename src/{objects => }/Sequence.cpp (61%) rename src/{objects => }/Sequence.h (92%) create mode 100644 src/tests/seq.t.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 98250b935..fcaecfe5d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,2 +1,2 @@ bin_PROGRAMS = task -task_SOURCES = Config.cpp Date.cpp Record.cpp T.cpp TDB.cpp Att.cpp Mod.cpp Table.cpp Grid.cpp Timer.cpp Duration.cpp StringTable.cpp color.cpp parse.cpp task.cpp command.cpp edit.cpp report.cpp util.cpp text.cpp rules.cpp import.cpp Config.h Date.h Record.h T.h TDB.h Att.h Mod.h Table.h Grid.h Timer.h Duration.h StringTable.h color.h task.h +task_SOURCES = Config.cpp Date.cpp Record.cpp T.cpp TDB.cpp Att.cpp Mod.cpp Sequence.cpp Table.cpp Grid.cpp Timer.cpp Duration.cpp StringTable.cpp color.cpp parse.cpp task.cpp command.cpp edit.cpp report.cpp util.cpp text.cpp rules.cpp import.cpp Config.h Date.h Record.h T.h TDB.h Att.h Mod.h Sequence.h Table.h Grid.h Timer.h Duration.h StringTable.h color.h task.h diff --git a/src/objects/Sequence.cpp b/src/Sequence.cpp similarity index 61% rename from src/objects/Sequence.cpp rename to src/Sequence.cpp index ca06f3be4..b4497ce13 100644 --- a/src/objects/Sequence.cpp +++ b/src/Sequence.cpp @@ -27,7 +27,9 @@ #include #include +#include #include "util.h" +#include "text.h" #include "Sequence.h" //////////////////////////////////////////////////////////////////////////////// @@ -36,20 +38,9 @@ Sequence::Sequence () } //////////////////////////////////////////////////////////////////////////////// -Sequence::Sequence (const Sequence& other) +Sequence::Sequence (const std::string& input) { - throw std::string ("unimplemented Sequence::Sequence"); -} - -//////////////////////////////////////////////////////////////////////////////// -Sequence& Sequence::operator= (const Sequence& other) -{ - throw std::string ("unimplemented Sequence::operator="); - if (this != &other) - { - } - - return *this; + parse (input); } //////////////////////////////////////////////////////////////////////////////// @@ -60,7 +51,51 @@ Sequence::~Sequence () //////////////////////////////////////////////////////////////////////////////// void Sequence::parse (const std::string& input) { - throw std::string ("unimplemented Sequence::parse"); + std::vector ranges; + split (ranges, input, ','); + + std::vector ::iterator it; + for (it = ranges.begin (); it != ranges.end (); ++it) + { + std::vector range; + split (range, *it, '-'); + + switch (range.size ()) + { + case 1: + { + if (! validId (range[0])) + throw std::string ("Invalid ID in sequence"); + + int id = ::atoi (range[0].c_str ()); + this->push_back (id); + } + break; + + case 2: + { + if (! validId (range[0]) || + ! validId (range[1])) + throw std::string ("Invalid ID in range"); + + int low = ::atoi (range[0].c_str ()); + int high = ::atoi (range[1].c_str ()); + if (low > high) + throw std::string ("Inverted sequence range high-low"); + + if (high - low > 1000) + throw std::string ("Range too large, exceeded 1,000 IDs"); + + for (int i = low; i <= high; ++i) + this->push_back (i); + } + break; + + default: + throw std::string ("Not a sequence."); + break; + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -81,3 +116,16 @@ void Sequence::combine (const Sequence& other) } //////////////////////////////////////////////////////////////////////////////// +bool Sequence::validId (const std::string& input) +{ + if (input.length () == 0) + return false; + + for (size_t i = 0; i < input.length (); ++i) + if (!::isdigit (input[i])) + return false; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/objects/Sequence.h b/src/Sequence.h similarity index 92% rename from src/objects/Sequence.h rename to src/Sequence.h index 85f417a2f..df9fa6143 100644 --- a/src/objects/Sequence.h +++ b/src/Sequence.h @@ -34,12 +34,14 @@ class Sequence : public std::vector { public: Sequence (); // Default constructor - Sequence (const Sequence&); // Copy constructor - Sequence& operator= (const Sequence&); // Assignment operator + Sequence (const std::string&); // Parse ~Sequence (); // Destructor void parse (const std::string&); void combine (const Sequence&); + +private: + bool validId (const std::string&); }; #endif diff --git a/src/task.h b/src/task.h index 3104c5d6d..1782de752 100644 --- a/src/task.h +++ b/src/task.h @@ -29,6 +29,8 @@ #include #include #include +#include "T.h" +#include "TDB.h" #include "Config.h" #include "Table.h" #include "Date.h" diff --git a/src/tests/.gitignore b/src/tests/.gitignore index 6f7048780..e6e519ed2 100644 --- a/src/tests/.gitignore +++ b/src/tests/.gitignore @@ -6,3 +6,4 @@ duration.t text.t autocomplete.t parse.t +seq.t diff --git a/src/tests/Makefile b/src/tests/Makefile index 6278cf5e7..4a32b3b1d 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -1,9 +1,9 @@ PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \ - parse.t + parse.t seq.t CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti LFLAGS = -L/usr/local/lib OBJECTS = ../TDB.o ../T.o ../parse.o ../text.o ../Date.o ../Duration.o \ - ../util.o ../Config.o + ../util.o ../Config.o ../Sequence.o all: $(PROJECT) @@ -43,3 +43,6 @@ autocomplete.t: autocomplete.t.o $(OBJECTS) test.o parse.t: parse.t.o $(OBJECTS) test.o g++ parse.t.o $(OBJECTS) test.o $(LFLAGS) -o parse.t +seq.t: seq.t.o $(OBJECTS) test.o + g++ seq.t.o $(OBJECTS) test.o $(LFLAGS) -o seq.t + diff --git a/src/tests/autocomplete.t.cpp b/src/tests/autocomplete.t.cpp index e97e2fb3e..aac9b31ec 100644 --- a/src/tests/autocomplete.t.cpp +++ b/src/tests/autocomplete.t.cpp @@ -26,7 +26,8 @@ //////////////////////////////////////////////////////////////////////////////// #include #include -#include <../task.h> +#include +#include //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) diff --git a/src/tests/duration.t.cpp b/src/tests/duration.t.cpp index c1c5e2108..ffd4ff39e 100644 --- a/src/tests/duration.t.cpp +++ b/src/tests/duration.t.cpp @@ -30,11 +30,11 @@ //////////////////////////////////////////////////////////////////////////////// // daily, day, Nd -// weekly, Nw, sennight, biweekly, fortnight +// weekly, 1w, sennight, biweekly, fortnight // monthly, bimonthly, Nm, semimonthly // 1st 2nd 3rd 4th .. 31st -// quarterly, Nq -// biannual, biyearly, annual, semiannual, yearly, Ny +// quarterly, 1q +// biannual, biyearly, annual, semiannual, yearly, 1y int convertDuration (const std::string& input) { diff --git a/src/tests/parse.t.cpp b/src/tests/parse.t.cpp index 3065cd952..430150e15 100644 --- a/src/tests/parse.t.cpp +++ b/src/tests/parse.t.cpp @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// #include #include "task.h" +#include "text.h" #include "test.h" //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tests/seq.t.cpp b/src/tests/seq.t.cpp new file mode 100644 index 000000000..90ff2f2d0 --- /dev/null +++ b/src/tests/seq.t.cpp @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////////// +// task - a command line task list manager. +// +// Copyright 2006 - 2009, Paul Beckingham. +// All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation; either version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the +// +// Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, +// Boston, MA +// 02110-1301 +// USA +// +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +Sequence parseSequence (const std::string& input) +{ + try { Sequence s (input); return s; } + catch (...) {} + return Sequence (); +} + +int main (int argc, char** argv) +{ + UnitTest t (18); + + // 1 + Sequence seq = parseSequence ("1"); + t.is (seq.size (), (size_t)1, "seq '1' -> 1 item"); + if (seq.size () == 1) + { + t.is (seq[0], 1, "seq '1' -> [0] == 1"); + } + else + { + t.fail ("seq '1' -> [0] == 1"); + } + + // 1,3 + seq = parseSequence ("1,3"); + t.is (seq.size (), (size_t)2, "seq '1,3' -> 2 items"); + if (seq.size () == 2) + { + t.is (seq[0], 1, "seq '1,3' -> [0] == 1"); + t.is (seq[1], 3, "seq '1,3' -> [1] == 3"); + } + else + { + t.fail ("seq '1,3' -> [0] == 1"); + t.fail ("seq '1,3' -> [1] == 3"); + } + + // 1,3-5,7 + seq = parseSequence ("1,3-5,7"); + t.is (seq.size (), (size_t)5, "seq '1,3-5,7' -> 5 items"); + if (seq.size () == 5) + { + t.is (seq[0], 1, "seq '1,3-5,7' -> [0] == 1"); + t.is (seq[1], 3, "seq '1,3-5,7' -> [1] == 3"); + t.is (seq[2], 4, "seq '1,3-5,7' -> [2] == 4"); + t.is (seq[3], 5, "seq '1,3-5,7' -> [3] == 5"); + t.is (seq[4], 7, "seq '1,3-5,7' -> [4] == 7"); + } + else + { + t.fail ("seq '1,3-5,7' -> [0] == 1"); + t.fail ("seq '1,3-5,7' -> [1] == 3"); + t.fail ("seq '1,3-5,7' -> [2] == 4"); + t.fail ("seq '1,3-5,7' -> [3] == 5"); + t.fail ("seq '1,3-5,7' -> [4] == 7"); + } + + // 1--2 + seq = parseSequence ("1--2"); + t.is (seq.size (), (size_t)0, "seq '1--2' -> 0 items (error)"); + + // 1-1000 + seq = parseSequence ("1-1000"); + t.is (seq.size (), (size_t)1000, "seq '1-1000' -> 1000 items"); + if (seq.size () == 1000) + { + t.is (seq[0], 1, "seq '1-1000' -> [0] == 1"); + t.is (seq[1], 2, "seq '1-1000' -> [1] == 3"); + t.is (seq[998], 999, "seq '1-1000' -> [998] == 999"); + t.is (seq[999], 1000, "seq '1-1000' -> [999] == 1000"); + } + else + { + t.fail ("seq '1-1000' -> [0] == 1"); + t.fail ("seq '1-1000' -> [1] == 2"); + t.fail ("seq '1-1000' -> [998] == 999"); + t.fail ("seq '1-1000' -> [999] == 1000"); + } + + // 1-1001 + seq = parseSequence ("1-1001"); + t.is (seq.size (), (size_t)0, "seq '1-1001' -> 0 items (error)"); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tests/t.benchmark.t.cpp b/src/tests/t.benchmark.t.cpp index 9adf4d578..857ab84aa 100644 --- a/src/tests/t.benchmark.t.cpp +++ b/src/tests/t.benchmark.t.cpp @@ -25,8 +25,8 @@ // //////////////////////////////////////////////////////////////////////////////// #include -#include "../T.h" -#include "../task.h" +#include "T.h" +#include "task.h" #include "test.h" //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tests/t.t.cpp b/src/tests/t.t.cpp index b0a30c6ae..64f11031a 100644 --- a/src/tests/t.t.cpp +++ b/src/tests/t.t.cpp @@ -24,8 +24,9 @@ // USA // //////////////////////////////////////////////////////////////////////////////// -#include "../T.h" -#include "../task.h" +#include "TDB.h" +#include "T.h" +#include "task.h" #include "test.h" //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tests/tdb.t.cpp b/src/tests/tdb.t.cpp index 4ef77694e..fd19d0ada 100644 --- a/src/tests/tdb.t.cpp +++ b/src/tests/tdb.t.cpp @@ -27,8 +27,8 @@ #include #include -#include "../TDB.h" -#include "../task.h" +#include "TDB.h" +#include "task.h" #include "test.h" //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tests/test.cpp b/src/tests/test.cpp index 4c8f201b3..f5245d2d8 100644 --- a/src/tests/test.cpp +++ b/src/tests/test.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "test.h" /////////////////////////////////////////////////////////////////////////////// diff --git a/src/tests/text.t.cpp b/src/tests/text.t.cpp index d959ea1d4..24da8ca2b 100644 --- a/src/tests/text.t.cpp +++ b/src/tests/text.t.cpp @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// #include #include "task.h" +#include "text.h" #include "test.h" ////////////////////////////////////////////////////////////////////////////////