From 6d32f8e493e7c191c811b940a825fc75b9089d7a Mon Sep 17 00:00:00 2001 From: "jamesclonk@jamesclonk.ch" Date: Fri, 3 Jan 2014 20:00:40 +0100 Subject: [PATCH] parsing todo.txt done --- README.md | 4 + testdata/example.go | 21 ++ testdata/expected_todo.txt | 40 ++- testdata/input_todo.txt | 5 +- testdata/ouput_todo.txt | 38 +++ todotxt.go | 85 +++-- todotxt_test.go | 672 ++++++++++++++++++++++++------------- 7 files changed, 601 insertions(+), 264 deletions(-) create mode 100644 testdata/example.go create mode 100644 testdata/ouput_todo.txt diff --git a/README.md b/README.md index 31c8f5b..ceccb07 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ It allows for parsing and manipulating of task lists and tasks in the todo.txt f go-todotxt requires Go1.1 or higher. +## Usage + +... + ## Documentation See [GoDoc - Documentation](https://godoc.org/github.com/JamesClonk/go-todotxt) for further documentation. diff --git a/testdata/example.go b/testdata/example.go new file mode 100644 index 0000000..20ddfdc --- /dev/null +++ b/testdata/example.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "github.com/JamesClonk/go-todotxt" + "log" +) + +func main() { + todotxt.IgnoreComments = false + + tasklist, err := todotxt.LoadFromFilename("../todo.txt") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Task 2, todo: %v\n", tasklist[1].Todo) + fmt.Printf("Task 3: %v\n", tasklist[2]) + fmt.Printf("Task 4, has priority: %v\n\n", tasklist[3].HasPriority()) + fmt.Print(tasklist) +} diff --git a/testdata/expected_todo.txt b/testdata/expected_todo.txt index 7cba68e..ac54598 100644 --- a/testdata/expected_todo.txt +++ b/testdata/expected_todo.txt @@ -1,10 +1,38 @@ -(A) 2012-01-30 Call Mom @Call @Phone +Family -(A) Schedule annual checkup +Health -(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 -(C) Add cover sheets @Office +TPSReports -Plan backyard herb garden @Home +Gardening +Improving +Planning 2013-02-22 Pick up milk @GroceryStore -Research self-publishing services +Novel due:2014-01-01 x Download Todo.txt mobile app @Phone +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +Plan backyard herb garden @Home +Gardening +Improving +Planning +(A) 2012-01-30 Call Mom @Call @Phone +Family +2013-02-22 Pick up milk @GroceryStore +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt +(C) Add cover sheets @Office +TPSReports +(A) 2012-01-30 Call Mom @Call @Phone +Family +(C) Add cover sheets @Office +TPSReports +Turn off TV @Electricity @Home @Of_Super-Importance @Television Importance:Very! +Research self-publishing services +Novel due:2014-01-01 +Plan backyard herb garden @Home +Gardening +Improving +Planning +Relaxing-Work +Research self-publishing services +Novel due:2014-01-01 Turn off TV @Electricity @Home @Television Importance:Very! +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +Turn off TV @Electricity @Home @Television Importance:Very! +x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12 +(A) 2012-01-30 Call Mom @Call @Phone +Family +x Create golang library @Go +go-todotxt due:2014-01-05 x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +xylophone lesson +X 2012-01-01 Make resolutions +x Download Todo.txt mobile app @Phone +x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05 +x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt +2013-02-22 Pick up milk @GroceryStore diff --git a/testdata/input_todo.txt b/testdata/input_todo.txt index 490f3a1..1f269fb 100644 --- a/testdata/input_todo.txt +++ b/testdata/input_todo.txt @@ -21,11 +21,11 @@ x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todot # Contexts test cases (A) 2012-01-30 @Phone Call Mom @Call +Family (C) Add cover sheets @Office +TPSReports -@Home Turn off TV @Electricity @Television @Electricity Importance:Very! +@Home Turn off TV @Electricity @Television @Electricity @Of_Super-Importance Importance:Very! Research self-publishing services +Novel +Novel +Novel due:2014-01-01 # Projects test cases -+Gardening Plan backyard herb garden +Planning @Home +Improving ++Gardening Plan backyard herb garden +Planning @Home +Improving +Relaxing-Work Research self-publishing services +Novel +Novel +Novel due:2014-01-01 @Home Turn off TV @Electricity @Television @Electricity Importance:Very! @@ -52,3 +52,4 @@ x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05 x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12 x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt +2013-02-22 Pick up milk @GroceryStore diff --git a/testdata/ouput_todo.txt b/testdata/ouput_todo.txt new file mode 100644 index 0000000..ac54598 --- /dev/null +++ b/testdata/ouput_todo.txt @@ -0,0 +1,38 @@ +2013-02-22 Pick up milk @GroceryStore +x Download Todo.txt mobile app @Phone +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +Plan backyard herb garden @Home +Gardening +Improving +Planning +(A) 2012-01-30 Call Mom @Call @Phone +Family +2013-02-22 Pick up milk @GroceryStore +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt +(C) Add cover sheets @Office +TPSReports +(A) 2012-01-30 Call Mom @Call @Phone +Family +(C) Add cover sheets @Office +TPSReports +Turn off TV @Electricity @Home @Of_Super-Importance @Television Importance:Very! +Research self-publishing services +Novel due:2014-01-01 +Plan backyard herb garden @Home +Gardening +Improving +Planning +Relaxing-Work +Research self-publishing services +Novel due:2014-01-01 +Turn off TV @Electricity @Home @Television Importance:Very! +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17 +Turn off TV @Electricity @Home @Television Importance:Very! +x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12 +(A) 2012-01-30 Call Mom @Call @Phone +Family +x Create golang library @Go +go-todotxt due:2014-01-05 +x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +xylophone lesson +X 2012-01-01 Make resolutions +x Download Todo.txt mobile app @Phone +x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05 +x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12 +x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt +x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt +2013-02-22 Pick up milk @GroceryStore diff --git a/todotxt.go b/todotxt.go index 432c28d..98968cb 100644 --- a/todotxt.go +++ b/todotxt.go @@ -11,6 +11,7 @@ package todotxt import ( "bufio" "fmt" + "io/ioutil" "os" "regexp" "sort" @@ -20,12 +21,12 @@ import ( // Task represents a todo.txt task entry. type Task struct { - Original string // Original raw task text - Todo string // Todo part of task text + Original string // Original raw task text. + Todo string // Todo part of task text. Priority string Projects []string Contexts []string - AdditionalTags map[string]string // Addon tags will be available here + AdditionalTags map[string]string // Addon tags will be available here. CreatedDate time.Time DueDate time.Time CompletedDate time.Time @@ -36,14 +37,19 @@ type Task struct { // It is usually loaded from a whole todo.txt file. type TaskList []Task +// IgnoreComments can be set to 'false', in order to revert to more standard todo.txt behaviour. +// The todo.txt format does not define comments. var ( - // Used for formatting time.Time into todo.txt date format. + // Used for formatting time.Time into todo.txt date format and vice-versa. DateLayout = "2006-01-02" + // Ignores comments (Lines/Text starting with "#"). + IgnoreComments = true // unexported vars priorityRx = regexp.MustCompile(`^(x|x \d{4}-\d{2}-\d{2}|)\s*\(([A-Z])\)\s+`) // Match priority: '(A) ...' or 'x (A) ...' or 'x 2012-12-12 (A) ...' // Match created date: '(A) 2012-12-12 ...' or 'x 2012-12-12 (A) 2012-12-12 ...' or 'x 2012-12-12 2012-12-12 ...' or '2012-12-12 ...' createdDateRx = regexp.MustCompile(`^(\([A-Z]\)|x \d{4}-\d{2}-\d{2} \([A-Z]\)|x \d{4}-\d{2}-\d{2}|)\s*(\d{4}-\d{2}-\d{2})\s+`) + completedRx = regexp.MustCompile(`^x\s+`) // Match completed: 'x ...' completedDateRx = regexp.MustCompile(`^x\s*(\d{4}-\d{2}-\d{2})\s+`) // Match completed date: 'x 2012-12-12 ...' addonTagRx = regexp.MustCompile(`(^|\s+)([\w-]+):(\S+)`) // Match additional tags date: '... due:2012-12-12 ...' contextRx = regexp.MustCompile(`(^|\s+)@(\S+)`) // Match contexts: '@Context ...' or '... @Context ...' @@ -51,8 +57,8 @@ var ( ) // String returns a complete tasklist string in todo.txt format. -func (tasklist *TaskList) String() (text string) { - for _, task := range *tasklist { +func (tasklist TaskList) String() (text string) { + for _, task := range tasklist { text += fmt.Sprintf("%s\n", task.String()) } return text @@ -66,7 +72,7 @@ func (tasklist *TaskList) String() (text string) { // // For example: // "(A) 2013-07-23 Call Dad @Home @Phone +Family due:2013-07-31 customTag1:Important!" -func (task *Task) String() string { +func (task Task) String() string { var text string if task.Completed { @@ -154,15 +160,16 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error { scanner := bufio.NewScanner(file) for scanner.Scan() { task := Task{} - task.Original = scanner.Text() + task.Original = strings.Trim(scanner.Text(), "\t\n\r ") // Read line + task.Todo = task.Original // Ignore blank or comment lines - if strings.Trim(task.Original, "\t\n\r ") == "" || strings.HasPrefix(task.Original, "#") { + if task.Todo == "" || (IgnoreComments && strings.HasPrefix(task.Todo, "#")) { continue } // Check for completed - if strings.HasPrefix(task.Original, "x ") { + if completedRx.MatchString(task.Original) { task.Completed = true // Check for completed date if completedDateRx.MatchString(task.Original) { @@ -172,11 +179,16 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error { task.CompletedDate = date } } + + // Remove from Todo text + task.Todo = completedDateRx.ReplaceAllString(task.Todo, "") // Strip CompletedDate first, otherwise it wouldn't match anymore (^x date...) + task.Todo = completedRx.ReplaceAllString(task.Todo, "") // Strip 'x ' } // Check for priority if priorityRx.MatchString(task.Original) { task.Priority = priorityRx.FindStringSubmatch(task.Original)[2] + task.Todo = priorityRx.ReplaceAllString(task.Todo, "") // Remove from Todo text } // Check for created date @@ -185,6 +197,7 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error { return err } else { task.CreatedDate = date + task.Todo = createdDateRx.ReplaceAllString(task.Todo, "") // Remove from Todo text } } @@ -207,11 +220,13 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error { // Check for contexts if contextRx.MatchString(task.Original) { task.Contexts = getSlice(contextRx) + task.Todo = contextRx.ReplaceAllString(task.Todo, "") // Remove from Todo text } // Check for projects if projectRx.MatchString(task.Original) { task.Projects = getSlice(projectRx) + task.Todo = projectRx.ReplaceAllString(task.Todo, "") // Remove from Todo text } // Check for additional tags @@ -231,20 +246,11 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error { } } task.AdditionalTags = tags + task.Todo = addonTagRx.ReplaceAllString(task.Todo, "") // Remove from Todo text } - text := task.Original - if task.Completed { - text = text[2:] // Strip 'x ' - text = completedDateRx.ReplaceAllString(text, "") - } - // Remove all matching regular expressions from text, so only the actual todo text is left - text = priorityRx.ReplaceAllString(text, "") - text = createdDateRx.ReplaceAllString(text, "") - text = contextRx.ReplaceAllString(text, "") - text = projectRx.ReplaceAllString(text, "") - text = addonTagRx.ReplaceAllString(text, "") - task.Todo = strings.Trim(text, "\t\n\r\f ") + // Trim any remaining whitespaces from Todo text + task.Todo = strings.Trim(task.Todo, "\t\n\r\f ") *tasklist = append(*tasklist, task) } @@ -255,6 +261,16 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error { return nil } +// WriteToFile writes a TaskList to *os.File. +// +// Using *os.File instead of a filename allows to also use os.Stdout. +func (tasklist *TaskList) WriteToFile(file *os.File) error { + writer := bufio.NewWriter(file) + _, err := writer.WriteString(tasklist.String()) + writer.Flush() + return err +} + // LoadFromFilename loads a TaskList from a file (most likely called "todo.txt"). // // Note: This will clear the current TaskList and overwrite it's contents with whatever is in the file. @@ -268,18 +284,35 @@ func (tasklist *TaskList) LoadFromFilename(filename string) error { return tasklist.LoadFromFile(file) } +// WriteToFilename writes a TaskList to the specified file (most likely called "todo.txt"). +func (tasklist *TaskList) WriteToFilename(filename string) error { + return ioutil.WriteFile(filename, []byte(tasklist.String()), 0644) +} + // LoadFromFile loads and returns a TaskList from *os.File. // // Using *os.File instead of a filename allows to also use os.Stdin. -func LoadFromFile(file *os.File) (*TaskList, error) { - tasklist := &TaskList{} +func LoadFromFile(file *os.File) (TaskList, error) { + tasklist := TaskList{} err := tasklist.LoadFromFile(file) return tasklist, err } +// WriteToFile writes a TaskList to *os.File. +// +// Using *os.File instead of a filename allows to also use os.Stdout. +func WriteToFile(tasklist *TaskList, file *os.File) error { + return tasklist.WriteToFile(file) +} + // LoadFromFilename loads and returns a TaskList from a file (most likely called "todo.txt"). -func LoadFromFilename(filename string) (*TaskList, error) { - tasklist := &TaskList{} +func LoadFromFilename(filename string) (TaskList, error) { + tasklist := TaskList{} err := tasklist.LoadFromFilename(filename) return tasklist, err } + +// WriteToFilename writes a TaskList to the specified file (most likely called "todo.txt"). +func WriteToFilename(tasklist *TaskList, filename string) error { + return tasklist.WriteToFilename(filename) +} diff --git a/todotxt_test.go b/todotxt_test.go index 8a82810..baf2b7f 100644 --- a/todotxt_test.go +++ b/todotxt_test.go @@ -11,308 +11,520 @@ import ( "time" ) +var ( + testInput = "testdata/input_todo.txt" + testOutput = "testdata/ouput_todo.txt" + testExpectedOutput = "testdata/expected_todo.txt" + testTasklist TaskList + testExpected interface{} + testGot interface{} +) + func TestLoadFromFile(t *testing.T) { - file, err := os.Open("testdata/input_todo.txt") + file, err := os.Open(testInput) if err != nil { t.Fatal(err) } defer file.Close() - if tasklist, err := LoadFromFile(file); err != nil { + if testTasklist, err := LoadFromFile(file); err != nil { t.Fatal(err) } else { - loadTest(t, *tasklist) + data, err := ioutil.ReadFile(testExpectedOutput) + if err != nil { + t.Fatal(err) + } + testExpected := string(data) + testGot := testTasklist.String() + if testGot != testExpected { + t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) + } } } func TestLoadFromFilename(t *testing.T) { - if tasklist, err := LoadFromFilename("testdata/input_todo.txt"); err != nil { + if testTasklist, err := LoadFromFilename(testInput); err != nil { t.Fatal(err) } else { - loadTest(t, *tasklist) + data, err := ioutil.ReadFile(testExpectedOutput) + if err != nil { + t.Fatal(err) + } + testExpected := string(data) + testGot := testTasklist.String() + if testGot != testExpected { + t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) + } } } -func loadTest(t *testing.T, tasklist TaskList) { - taskId := 1 - var expected, got interface{} +func TestWriteFile(t *testing.T) { + os.Remove(testOutput) + os.Create(testOutput) var err error - // ------------------------------------------------------------------------------------- - // complete tasklist string - data, err := ioutil.ReadFile("testdata/expected_todo.txt") + fileInput, err := os.Open(testInput) if err != nil { t.Fatal(err) } - expected = string(data) - got = tasklist.String() - if got != expected { - //t.Errorf("Expected TaskList to be [%s], but got [%s]", expected, got)-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - } - - // ------------------------------------------------------------------------------------- - // count tasks - expected = 10 - got = len(tasklist) - if got != expected { - t.Errorf("Expected TaskList to contain %d tasks, but got %d", expected, got) - } - - // ------------------------------------------------------------------------------------- - // complete task strings - expected = "2013-02-22 Pick up milk @GroceryStore" - got = tasklist[taskId-1].String() - if got != expected { - t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, expected, got) - } - taskId++ - - expected = "x Download Todo.txt mobile app @Phone" - got = tasklist[taskId-1].String() - if got != expected { - t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, expected, got) - } - taskId++ - - expected = "(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17" - got = tasklist[taskId-1].Task() - if got != expected { - t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, expected, got) - } - taskId++ - - expected = "x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt" - got = tasklist[taskId-1].Task() - if got != expected { - t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, expected, got) - } - expected = "1" - got = tasklist[taskId-1].Todo - if got != expected { - t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, expected, got) - } - taskId++ - - expected = "x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt" - got = tasklist[taskId-1].Task() - if got != expected { - t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, expected, got) - } - taskId++ - - // ------------------------------------------------------------------------------------- - // task priority - expected = "B" - got = tasklist[taskId-1].Priority - if got != expected { - t.Errorf("Expected Task[%d] to have priority '%s', but got '%s'", taskId, expected, got) - } - taskId++ - - expected = "C" - got = tasklist[taskId-1].Priority - if got != expected { - t.Errorf("Expected Task[%d] to have priority '%s', but got '%s'", taskId, expected, got) - } - taskId++ - - expected = "B" - got = tasklist[taskId-1].Priority - if got != expected { - t.Errorf("Expected Task[%d] to have priority '%s', but got '%s'", taskId, expected, got) - } - taskId++ - - if tasklist[taskId-1].HasPriority() { - t.Errorf("Expected Task[%d] to have no priority, but got '%s'", taskId, tasklist[4].Priority) - } - taskId++ - - // ------------------------------------------------------------------------------------- - // task created date - expected, err = time.Parse(DateLayout, "2012-01-30") + defer fileInput.Close() + fileOutput, err := os.OpenFile(testOutput, os.O_RDWR, 0644) if err != nil { t.Fatal(err) } - got = tasklist[taskId-1].CreatedDate - if got != expected { - t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, expected, got) - } - taskId++ + defer fileInput.Close() - expected, err = time.Parse(DateLayout, "2013-02-22") + if testTasklist, err = LoadFromFile(fileInput); err != nil { + t.Fatal(err) + } + if err = WriteToFile(&testTasklist, fileOutput); err != nil { + t.Fatal(err) + } + fileInput.Close() + fileOutput, err = os.Open(testOutput) if err != nil { t.Fatal(err) } - got = tasklist[taskId-1].CreatedDate - if got != expected { - t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, expected, got) + if testTasklist, err = LoadFromFile(fileOutput); err != nil { + t.Fatal(err) } - taskId++ - expected, err = time.Parse(DateLayout, "2013-12-30") + data, err := ioutil.ReadFile(testExpectedOutput) if err != nil { t.Fatal(err) } - got = tasklist[taskId-1].CreatedDate - if got != expected { - t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, expected, got) + testExpected := string(data) + testGot := testTasklist.String() + if testGot != testExpected { + t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) } - taskId++ +} - expected, err = time.Parse(DateLayout, "2014-01-01") +func TestTaskListWriteFile(t *testing.T) { + os.Remove(testOutput) + os.Create(testOutput) + testTasklist := TaskList{} + + fileInput, err := os.Open(testInput) if err != nil { t.Fatal(err) } - got = tasklist[taskId-1].CreatedDate - if got != expected { - t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, expected, got) - } - taskId++ - - if tasklist[taskId-1].HasCreatedDate() { - t.Errorf("Expected Task[%d] to have no created date, but got '%v'", taskId, tasklist[4].CreatedDate) - } - taskId++ - - // ------------------------------------------------------------------------------------- - // task contexts - expected = []string{"Call", "Phone"} - got = tasklist[taskId-1].Contexts - if !compareSlices(got.([]string), expected.([]string)) { - t.Errorf("Expected Task[%d] to have contexts '%v', but got '%v'", taskId, expected, got) - } - taskId++ - - expected = []string{"Office"} - got = tasklist[taskId-1].Contexts - if !compareSlices(got.([]string), expected.([]string)) { - t.Errorf("Expected Task[%d] to have contexts '%v', but got '%v'", taskId, expected, got) - } - taskId++ - - expected = []string{"Electricity", "Home", "Television"} - got = tasklist[taskId-1].Contexts - if !compareSlices(got.([]string), expected.([]string)) { - t.Errorf("Expected Task[%d] to have contexts '%v', but got '%v'", taskId, expected, got) - } - taskId++ - - expected = []string{} - got = tasklist[taskId-1].Contexts - if !compareSlices(got.([]string), expected.([]string)) { - t.Errorf("Expected Task[%d] to have no contexts, but got '%v'", taskId, got) - } - taskId++ - - // ------------------------------------------------------------------------------------- - // task projects - expected = []string{"Gardening", "Improving", "Planning"} - got = tasklist[taskId-1].Projects - if !compareSlices(got.([]string), expected.([]string)) { - t.Errorf("Expected Task[%d] to have projects '%v', but got '%v'", taskId, expected, got) - } - taskId++ - - expected = []string{"Novel"} - got = tasklist[taskId-1].Projects - if !compareSlices(got.([]string), expected.([]string)) { - t.Errorf("Expected Task[%d] to have projects '%v', but got '%v'", taskId, expected, got) - } - taskId++ - - expected = []string{} - got = tasklist[taskId-1].Projects - if !compareSlices(got.([]string), expected.([]string)) { - t.Errorf("Expected Task[%d] to have no projects, but got '%v'", taskId, got) - } - taskId++ - - // ------------------------------------------------------------------------------------- - // task due date - expected, err = time.Parse(DateLayout, "2014-02-17") + defer fileInput.Close() + fileOutput, err := os.OpenFile(testOutput, os.O_RDWR, 0644) if err != nil { t.Fatal(err) } - got = tasklist[taskId-1].DueDate - if got != expected { - t.Errorf("Expected Task[%d] to have due date '%s', but got '%v'", taskId, expected, got) + defer fileInput.Close() + + if err := testTasklist.LoadFromFile(fileInput); err != nil { + t.Fatal(err) + } + if err := testTasklist.WriteToFile(fileOutput); err != nil { + t.Fatal(err) + } + fileInput.Close() + fileOutput, err = os.Open(testOutput) + if err != nil { + t.Fatal(err) + } + if err := testTasklist.LoadFromFile(fileOutput); err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadFile(testExpectedOutput) + if err != nil { + t.Fatal(err) + } + testExpected := string(data) + testGot := testTasklist.String() + if testGot != testExpected { + t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) + } +} + +func TestWriteFilename(t *testing.T) { + os.Remove(testOutput) + var err error + + if testTasklist, err = LoadFromFilename(testInput); err != nil { + t.Fatal(err) + } + if err = WriteToFilename(&testTasklist, testOutput); err != nil { + t.Fatal(err) + } + if testTasklist, err = LoadFromFilename(testOutput); err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadFile(testExpectedOutput) + if err != nil { + t.Fatal(err) + } + testExpected := string(data) + testGot := testTasklist.String() + if testGot != testExpected { + t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) + } +} + +func TestTaskListWriteFilename(t *testing.T) { + os.Remove(testOutput) + testTasklist := TaskList{} + + if err := testTasklist.LoadFromFilename(testInput); err != nil { + t.Fatal(err) + } + if err := testTasklist.WriteToFilename(testOutput); err != nil { + t.Fatal(err) + } + if err := testTasklist.LoadFromFilename(testOutput); err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadFile(testExpectedOutput) + if err != nil { + t.Fatal(err) + } + testExpected := string(data) + testGot := testTasklist.String() + if testGot != testExpected { + t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) + } +} + +func TestTaskListCount(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + + testExpected := 38 + testGot := len(testTasklist) + if testGot != testExpected { + t.Errorf("Expected TaskList to contain %d tasks, but got %d", testExpected, testGot) + } +} + +func TestTaskString(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 1 + + testExpected = "2013-02-22 Pick up milk @GroceryStore" + testGot = testTasklist[taskId-1].String() + if testGot != testExpected { + t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, testExpected, testGot) } taskId++ - if tasklist[taskId-1].HasDueDate() { - t.Errorf("Expected Task[%d] to have no due date, but got '%v'", taskId, tasklist[taskId-1].DueDate) + testExpected = "x Download Todo.txt mobile app @Phone" + testGot = testTasklist[taskId-1].String() + if testGot != testExpected { + t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, testExpected, testGot) } taskId++ - // ------------------------------------------------------------------------------------- - // task addon tags - expected = map[string]string{"Level": "5", "private": "false"} - got = tasklist[taskId-1].AdditionalTags - if len(got.(map[string]string)) != 2 || - !compareMaps(got.(map[string]string), expected.(map[string]string)) { - t.Errorf("Expected Task[%d] to have addon tags '%v', but got '%v'", taskId, expected, got) + testExpected = "(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17" + testGot = testTasklist[taskId-1].Task() + if testGot != testExpected { + t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, testExpected, testGot) } taskId++ - expected = map[string]string{"Importance": "Very!"} - got = tasklist[taskId-1].AdditionalTags - if len(got.(map[string]string)) != 1 || - !compareMaps(got.(map[string]string), expected.(map[string]string)) { - t.Errorf("Expected Task[%d] to have projects '%v', but got '%v'", taskId, expected, got) + testExpected = "x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt" + testGot = testTasklist[taskId-1].Task() + if testGot != testExpected { + t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, testExpected, testGot) } taskId++ - expected = map[string]string{} - got = tasklist[taskId-1].AdditionalTags - if len(got.(map[string]string)) != 0 || - !compareMaps(got.(map[string]string), expected.(map[string]string)) { - t.Errorf("Expected Task[%d] to have no additional tags, but got '%v'", taskId, got) + testExpected = "x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt" + testGot = testTasklist[taskId-1].Task() + if testGot != testExpected { + t.Errorf("Expected Task[%d] to be [%s], but got [%s]", taskId, testExpected, testGot) + } + taskId++ +} + +func TestTaskPriority(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 6 + + testExpected = "B" + testGot = testTasklist[taskId-1].Priority + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have priority '%s', but got '%s'", taskId, testExpected, testGot) } taskId++ - expected = map[string]string{} - got = tasklist[taskId-1].AdditionalTags - if len(got.(map[string]string)) != 0 || - !compareMaps(got.(map[string]string), expected.(map[string]string)) { - t.Errorf("Expected Task[%d] to have no additional tags, but got '%v'", taskId, got) + testExpected = "C" + testGot = testTasklist[taskId-1].Priority + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have priority '%s', but got '%s'", taskId, testExpected, testGot) } taskId++ - // ------------------------------------------------------------------------------------- - // task completed - expected = true - got = tasklist[taskId-1].Completed - if got != expected { - t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, got) + testExpected = "B" + testGot = testTasklist[taskId-1].Priority + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have priority '%s', but got '%s'", taskId, testExpected, testGot) } taskId++ - expected = true - got = tasklist[taskId-1].Completed - if got != expected { - t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, got) + if testTasklist[taskId-1].HasPriority() { + t.Errorf("Expected Task[%d] to have no priority, but got '%s'", taskId, testTasklist[4].Priority) + } + taskId++ +} + +func TestTaskCreatedDate(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 10 + + testExpected, err := time.Parse(DateLayout, "2012-01-30") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].CreatedDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, testExpected, testGot) } taskId++ - expected = true - got = tasklist[taskId-1].Completed - if got != expected { - t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, got) + testExpected, err = time.Parse(DateLayout, "2013-02-22") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].CreatedDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, testExpected, testGot) } taskId++ - expected = false - got = tasklist[taskId-1].Completed - if got != expected { - t.Errorf("Expected Task[%d] to not be completed, but got '%v'", taskId, got) + testExpected, err = time.Parse(DateLayout, "2013-12-30") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].CreatedDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, testExpected, testGot) } taskId++ - expected = false - got = tasklist[taskId-1].Completed - if got != expected { - t.Errorf("Expected Task[%d] to not be completed, but got '%v'", taskId, got) + testExpected, err = time.Parse(DateLayout, "2014-01-01") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].CreatedDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have created date '%s', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + if testTasklist[taskId-1].HasCreatedDate() { + t.Errorf("Expected Task[%d] to have no created date, but got '%v'", taskId, testTasklist[4].CreatedDate) + } + taskId++ +} + +func TestTaskContexts(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 15 + + testExpected = []string{"Call", "Phone"} + testGot = testTasklist[taskId-1].Contexts + if !compareSlices(testGot.([]string), testExpected.([]string)) { + t.Errorf("Expected Task[%d] to have contexts '%v', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected = []string{"Office"} + testGot = testTasklist[taskId-1].Contexts + if !compareSlices(testGot.([]string), testExpected.([]string)) { + t.Errorf("Expected Task[%d] to have contexts '%v', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected = []string{"Electricity", "Home", "Of_Super-Importance", "Television"} + testGot = testTasklist[taskId-1].Contexts + if !compareSlices(testGot.([]string), testExpected.([]string)) { + t.Errorf("Expected Task[%d] to have contexts '%v', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected = []string{} + testGot = testTasklist[taskId-1].Contexts + if !compareSlices(testGot.([]string), testExpected.([]string)) { + t.Errorf("Expected Task[%d] to have no contexts, but got '%v'", taskId, testGot) + } + taskId++ +} + +func TestTasksProjects(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 19 + + testExpected = []string{"Gardening", "Improving", "Planning", "Relaxing-Work"} + testGot = testTasklist[taskId-1].Projects + if !compareSlices(testGot.([]string), testExpected.([]string)) { + t.Errorf("Expected Task[%d] to have projects '%v', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected = []string{"Novel"} + testGot = testTasklist[taskId-1].Projects + if !compareSlices(testGot.([]string), testExpected.([]string)) { + t.Errorf("Expected Task[%d] to have projects '%v', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected = []string{} + testGot = testTasklist[taskId-1].Projects + if !compareSlices(testGot.([]string), testExpected.([]string)) { + t.Errorf("Expected Task[%d] to have no projects, but got '%v'", taskId, testGot) + } + taskId++ +} + +func TestTaskDueDate(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 22 + + testExpected, err := time.Parse(DateLayout, "2014-02-17") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].DueDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have due date '%s', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + if testTasklist[taskId-1].HasDueDate() { + t.Errorf("Expected Task[%d] to have no due date, but got '%v'", taskId, testTasklist[taskId-1].DueDate) + } + taskId++ +} + +func TestTaskAddonTags(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 24 + + testExpected = map[string]string{"Level": "5", "private": "false"} + testGot = testTasklist[taskId-1].AdditionalTags + if len(testGot.(map[string]string)) != 2 || + !compareMaps(testGot.(map[string]string), testExpected.(map[string]string)) { + t.Errorf("Expected Task[%d] to have addon tags '%v', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected = map[string]string{"Importance": "Very!"} + testGot = testTasklist[taskId-1].AdditionalTags + if len(testGot.(map[string]string)) != 1 || + !compareMaps(testGot.(map[string]string), testExpected.(map[string]string)) { + t.Errorf("Expected Task[%d] to have projects '%v', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected = map[string]string{} + testGot = testTasklist[taskId-1].AdditionalTags + if len(testGot.(map[string]string)) != 0 || + !compareMaps(testGot.(map[string]string), testExpected.(map[string]string)) { + t.Errorf("Expected Task[%d] to have no additional tags, but got '%v'", taskId, testGot) + } + taskId++ + + testExpected = map[string]string{} + testGot = testTasklist[taskId-1].AdditionalTags + if len(testGot.(map[string]string)) != 0 || + !compareMaps(testGot.(map[string]string), testExpected.(map[string]string)) { + t.Errorf("Expected Task[%d] to have no additional tags, but got '%v'", taskId, testGot) + } + taskId++ +} + +func TestTaskCompleted(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 28 + + testExpected = true + testGot = testTasklist[taskId-1].Completed + if testGot != testExpected { + t.Errorf("Expected Task[%d] not to completed, but got '%v'", taskId, testGot) + } + taskId++ + + testExpected = true + testGot = testTasklist[taskId-1].Completed + if testGot != testExpected { + t.Errorf("Expected Task[%d] not to completed, but got '%v'", taskId, testGot) + } + taskId++ + + testExpected = true + testGot = testTasklist[taskId-1].Completed + if testGot != testExpected { + t.Errorf("Expected Task[%d] not to completed, but got '%v'", taskId, testGot) + } + taskId++ + + testExpected = false + testGot = testTasklist[taskId-1].Completed + if testGot != testExpected { + t.Errorf("Expected Task[%d] not to be completed, but got '%v'", taskId, testGot) + } + taskId++ + + testExpected = false + testGot = testTasklist[taskId-1].Completed + if testGot != testExpected { + t.Errorf("Expected Task[%d] not to be completed, but got '%v'", taskId, testGot) + } + taskId++ +} + +func TestTaskCompletedDate(t *testing.T) { + testTasklist.LoadFromFilename(testInput) + taskId := 33 + + if testTasklist[taskId-1].HasCompletedDate() { + t.Errorf("Expected Task[%d] to not have a completed date, but got '%v'", taskId, testTasklist[taskId-1].CompletedDate) + } + taskId++ + + testExpected, err := time.Parse(DateLayout, "2014-01-03") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].CompletedDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have completed date '%s', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + if testTasklist[taskId-1].HasCompletedDate() { + t.Errorf("Expected Task[%d] to not have a completed date, but got '%v'", taskId, testTasklist[taskId-1].CompletedDate) + } + taskId++ + + testExpected, err = time.Parse(DateLayout, "2014-01-02") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].CompletedDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have completed date '%s', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + testExpected, err = time.Parse(DateLayout, "2014-01-03") + if err != nil { + t.Fatal(err) + } + testGot = testTasklist[taskId-1].CompletedDate + if testGot != testExpected { + t.Errorf("Expected Task[%d] to have completed date '%s', but got '%v'", taskId, testExpected, testGot) + } + taskId++ + + if testTasklist[taskId-1].HasCompletedDate() { + t.Errorf("Expected Task[%d] to not have a completed date, but got '%v'", taskId, testTasklist[taskId-1].CompletedDate) } taskId++ }