Compare commits
107 Commits
v2.1.0
...
wip-weekly
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c963ba41ae | ||
|
|
b4ef59b637 | ||
|
|
90b80268d3 | ||
|
|
7dde1fbab6 | ||
|
|
b3e0f791a7 | ||
|
|
732e45aeb1 | ||
|
|
a53bb81b4c | ||
|
|
29a470f97d | ||
|
|
6ab0004fe8 | ||
|
|
15acb054f5 | ||
|
|
5f3dadee5c | ||
|
|
20c6f44784 | ||
|
|
9bab224a29 | ||
|
|
4d3b7472ff | ||
|
|
680e93e737 | ||
|
|
477738828f | ||
|
|
b9f95633dc | ||
|
|
df1e2eb7cf | ||
|
|
a4e68f9c3f | ||
|
|
3028de42a8 | ||
|
|
8fceae171d | ||
|
|
9898e7df3f | ||
|
|
42e1a658d6 | ||
|
|
a075adb4ec | ||
|
|
f1caecec4e | ||
|
|
078c69496f | ||
|
|
01a250c702 | ||
|
|
1f17672215 | ||
|
|
40e0da5108 | ||
|
|
56dfae0486 | ||
|
|
8549eef46b | ||
|
|
8567a90e4c | ||
|
|
55f45e8515 | ||
|
|
03ccc73703 | ||
|
|
a822560d44 | ||
|
|
d860c2c36e | ||
|
|
7f954d73ae | ||
|
|
8e864568a9 | ||
|
|
df4f9150cf | ||
|
|
5cc988102d | ||
|
|
3a0fd43270 | ||
|
|
1a6ff81e28 | ||
|
|
5491e458a2 | ||
|
|
5789f5b4c2 | ||
|
|
b17cb11ec6 | ||
|
|
0b3d9109de | ||
|
|
ad40ef0f18 | ||
|
|
31216fe365 | ||
|
|
7f5c8fb3e1 | ||
|
|
3e7b60abcd | ||
|
|
cc3e5f73aa | ||
|
|
52604ebf78 | ||
|
|
bbe153b9bb | ||
|
|
ec54a032cb | ||
|
|
758cdc5551 | ||
|
|
02dc030225 | ||
|
|
7b769b2eea | ||
|
|
39ee9ab045 | ||
|
|
1e5902d0e2 | ||
|
|
3df7497287 | ||
|
|
07bb979d43 | ||
|
|
7e04849a4f | ||
|
|
f8b2646b92 | ||
|
|
02980ae7ee | ||
|
|
12bbf8fe67 | ||
|
|
ab78607506 | ||
|
|
6be78ca5fa | ||
|
|
9ab77253db | ||
|
|
cf3c5312bf | ||
|
|
37a7bb0e8a | ||
|
|
ed8e8e24d9 | ||
|
|
d6f00ca42f | ||
|
|
a03a3bf66b | ||
|
|
448cecb91d | ||
|
|
ee59233c36 | ||
|
|
f55f5e8b5f | ||
|
|
d508ed9dee | ||
|
|
62f3313ff9 | ||
|
|
6bc374c5f2 | ||
|
|
87959a8aa8 | ||
|
|
3e9d40ebd7 | ||
|
|
6bc05000d9 | ||
|
|
717f052f13 | ||
|
|
4ee8c332ed | ||
|
|
88caf44e9e | ||
|
|
db66767170 | ||
|
|
f8f8e83c40 | ||
|
|
2648bb047c | ||
|
|
bbff2d13bb | ||
|
|
e4c7979888 | ||
|
|
47c7ba75b3 | ||
|
|
2bd2e9f7bd | ||
|
|
f37cedc7ca | ||
|
|
7b2c9f080a | ||
|
|
98646a575a | ||
|
|
e6649e6293 | ||
|
|
eb61752708 | ||
|
|
5683490c0e | ||
|
|
20e6892775 | ||
|
|
fd9b002ce1 | ||
|
|
7736e6b4fa | ||
|
|
586abe8282 | ||
|
|
7bd6696540 | ||
|
|
b5a03cfee5 | ||
|
|
d7b9f87994 | ||
|
|
959598416f | ||
|
|
25c6505007 |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
VERSION-FILE
|
||||
tests/test-results
|
||||
tests/trash\ directory.*
|
||||
10
.todo.actions.d/README
Normal file
10
.todo.actions.d/README
Normal file
@@ -0,0 +1,10 @@
|
||||
TODO.TXT CLI Add-ons
|
||||
|
||||
adda (symlink to aa for fewer keystrokes)
|
||||
* Adds a task and prioritizes it A in one shot
|
||||
|
||||
addx (symlink ax for fewer keystrokes)
|
||||
* Adds a task and marks it as complete in one shot
|
||||
|
||||
birdseye (requires Python in path and birdseye.py file)
|
||||
* Generates a textual report of open and complete tasks in all contexts and projects
|
||||
20
.todo.actions.d/adda
Executable file
20
.todo.actions.d/adda
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
action=$1
|
||||
shift
|
||||
|
||||
[ "$action" = "usage" ] && {
|
||||
echo " Add and prioritize A:"
|
||||
curcmd=`basename $0`
|
||||
echo " $curcmd \"THING I NEED TO DO +project @context\""
|
||||
echo " Add an item and prioritize it A in one step"
|
||||
echo ""
|
||||
exit
|
||||
}
|
||||
|
||||
if "$TODO_SH" command add "$@"; then
|
||||
# figure out the line of what we just added, and prioritize it A
|
||||
line=`sed -n '$ =' "$TODO_FILE"`
|
||||
echo "$line"
|
||||
"$TODO_SH" command pri "$line" A
|
||||
fi
|
||||
20
.todo.actions.d/addx
Executable file
20
.todo.actions.d/addx
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
action=$1
|
||||
shift
|
||||
|
||||
[ "$action" = "usage" ] && {
|
||||
echo " Add and do:"
|
||||
curcmd=`basename $0`
|
||||
echo " $curcmd \"THING I DID +project @context\""
|
||||
echo " Add an item and mark it as done in one step"
|
||||
echo ""
|
||||
exit
|
||||
}
|
||||
|
||||
if "$TODO_SH" command add "$@"; then
|
||||
# figure out the line of what we just added, and prioritize it A
|
||||
line=`sed -n '$ =' "$TODO_FILE"`
|
||||
echo "$line"
|
||||
"$TODO_SH" command do "$line"
|
||||
fi
|
||||
16
.todo.actions.d/birdseye
Executable file
16
.todo.actions.d/birdseye
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
action=$1
|
||||
shift
|
||||
|
||||
[ "$action" = "usage" ] && {
|
||||
echo " Bird's eye report:"
|
||||
echo " birdseye"
|
||||
echo " generates a textual report of pending and completed tasks in all projects and contexts"
|
||||
echo ""
|
||||
exit
|
||||
}
|
||||
|
||||
[ "$action" = "birdseye" ] && {
|
||||
python ${TODO_ACTIONS_DIR}/birdseye.py "$TODO_FILE" "$DONE_FILE"
|
||||
}
|
||||
202
.todo.actions.d/birdseye.py
Executable file
202
.todo.actions.d/birdseye.py
Executable file
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
""" TODO.TXT Bird's Eye View Reporter
|
||||
USAGE:
|
||||
birdseye.py [todo.txt] [done.txt]
|
||||
|
||||
USAGE NOTES:
|
||||
Expects two text files as parameters, each of which formatted as follows:
|
||||
- One todo per line, ie, "call Mom"
|
||||
- with an optional project association indicated as such: "+projectname"
|
||||
- with the context in which the tasks should be completed, indicated as such: "@context"
|
||||
- with the task priority optionally listed at the front of the line, in parens, ie, "(A)"
|
||||
|
||||
For example, 4 lines of todo.txt might look like this:
|
||||
|
||||
+garagesale @phone schedule Goodwill pickup
|
||||
(A) @phone Tell Mom I love her
|
||||
+writing draft Great American Novel
|
||||
(B) smell the roses
|
||||
|
||||
The done.txt file is a list of completed todos from todo.txt.
|
||||
|
||||
See more on todo.txt here:
|
||||
http://todotxt.com
|
||||
|
||||
|
||||
OUTPUT:
|
||||
Displays a list of:
|
||||
- working projects and their percentage complete
|
||||
- contexts in which open todos exist
|
||||
- contexts and projects with tasks that have been prioritized
|
||||
- projects which are completely done (don't have any open todos)
|
||||
|
||||
CHANGELOG:
|
||||
2016.03.17 - Update for Python 3. Tx, JonathanReeve!
|
||||
2006.07.29 - Now supports p:, p- and + project notation. Tx, Pedro!
|
||||
2006.05.02 - Released
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
__version__ = "1.2"
|
||||
__date__ = "2006/05/02"
|
||||
__updated__ = "2016/03/17"
|
||||
__author__ = "Gina Trapani (ginatrapani@gmail.com)"
|
||||
__copyright__ = "Copyright 2006 - 2016, Gina Trapani"
|
||||
__license__ = "GPL"
|
||||
__history__ = """
|
||||
1.2 - Update for Python 3. Tx, JonathanReeve!
|
||||
1.1 - Now supports p:, p- and + project notation. Tx, Pedro!
|
||||
1.0 - Released.
|
||||
"""
|
||||
|
||||
def usage():
|
||||
print("USAGE: %s [todo.txt] [done.txt]" % (sys.argv[0], ))
|
||||
|
||||
def printTaskGroups(title, taskDict, priorityList, percentages):
|
||||
print("")
|
||||
print("%s"% (title,))
|
||||
separator("-")
|
||||
if not taskDict:
|
||||
print("No items to list.")
|
||||
else:
|
||||
# sort the dictionary by value
|
||||
# http://python.fyxm.net/peps/pep-0265.html
|
||||
items = [(v, k) for k, v in list(taskDict.items())]
|
||||
items.sort()
|
||||
items.reverse() # so largest is first
|
||||
items = [(k, v) for v, k in items]
|
||||
|
||||
for item in items:
|
||||
if item[0] in priorityList:
|
||||
if item[0] not in percentages:
|
||||
printTaskGroup(item, -1, "*")
|
||||
else:
|
||||
printTaskGroup(item, percentages[item[0]], "*")
|
||||
|
||||
for item in items:
|
||||
if item[0] not in priorityList:
|
||||
if item[0] not in percentages:
|
||||
printTaskGroup(item, -1, " ")
|
||||
else:
|
||||
printTaskGroup(item, percentages[item[0]], " ")
|
||||
|
||||
def printTaskGroup(p, pctage, star):
|
||||
if pctage > -1:
|
||||
progressBar = ""
|
||||
numStars = int(pctage//10)
|
||||
progressBar = "=" * numStars
|
||||
numSpaces = 10 - numStars
|
||||
for n in range(numSpaces):
|
||||
progressBar += " "
|
||||
|
||||
if pctage > 9:
|
||||
displayTotal = " %d%%"% (pctage, );
|
||||
else:
|
||||
displayTotal = " %d%%"% (pctage, );
|
||||
print("%s %s [%s] %s (%d todos)"% (star, displayTotal, progressBar, p[0], p[1],))
|
||||
else:
|
||||
print("%s %s (%d todos)"% (star, p[0], p[1], ))
|
||||
|
||||
def separator(c):
|
||||
sep = ""
|
||||
sep = c * 42
|
||||
print(sep)
|
||||
|
||||
|
||||
def main(argv):
|
||||
# make sure you have all your args
|
||||
if len(argv) < 2:
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
# process todo.txt
|
||||
try:
|
||||
f = open (argv[0], "r")
|
||||
projects = {}
|
||||
contexts = {}
|
||||
projectPriority = []
|
||||
contextPriority = []
|
||||
for line in f:
|
||||
prioritized = False
|
||||
words = line.split()
|
||||
if words and words[0].startswith("("):
|
||||
prioritized = True
|
||||
for word in words:
|
||||
if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+":
|
||||
if word not in projects:
|
||||
projects[word] = 1
|
||||
else:
|
||||
projects[word] = projects.setdefault(word,0) + 1
|
||||
if prioritized:
|
||||
projectPriority.append(word)
|
||||
if word[0:1] == "@":
|
||||
if word not in contexts:
|
||||
contexts[word] = 1
|
||||
else:
|
||||
contexts[word] = contexts.setdefault(word, 0) + 1
|
||||
if prioritized:
|
||||
contextPriority.append(word)
|
||||
f.close()
|
||||
except IOError:
|
||||
print("ERROR: The file named %s could not be read."% (argv[0], ))
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
# process done.txt
|
||||
try:
|
||||
completedTasks = {}
|
||||
f = open (argv[1], "r")
|
||||
for line in f:
|
||||
words = line.split()
|
||||
for word in words:
|
||||
if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+":
|
||||
if word not in completedTasks:
|
||||
completedTasks[word] = 1
|
||||
else:
|
||||
completedTasks[word] = completedTasks.setdefault(word, 0) + 1
|
||||
f.close()
|
||||
except IOError:
|
||||
print("ERROR: The file named %s could not be read."% (argv[1], ))
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
# calculate percentages
|
||||
projectPercentages = {}
|
||||
for project in projects:
|
||||
openTasks = projects[project]
|
||||
if project in completedTasks:
|
||||
closedTasks = completedTasks[project]
|
||||
else:
|
||||
closedTasks = 0
|
||||
totalTasks = openTasks + closedTasks
|
||||
projectPercentages[project] = (closedTasks*100) / totalTasks
|
||||
|
||||
# get projects all done
|
||||
projectsWithNoIncompletes = {}
|
||||
for task in completedTasks:
|
||||
if task not in projects:
|
||||
projectsWithNoIncompletes[task] = 0
|
||||
|
||||
# print out useful info
|
||||
#print "TODO.TXT Bird's Eye View Report %s"% ( datetime.date.today().isoformat(), )
|
||||
print("")
|
||||
print("TODO.TXT Bird's Eye View Report")
|
||||
|
||||
separator("=")
|
||||
|
||||
printTaskGroups("Projects with Open TODOs", projects, projectPriority, projectPercentages)
|
||||
printTaskGroups("Contexts with Open TODOs", contexts, contextPriority, projectPercentages)
|
||||
printTaskGroups("Completed Projects (No open TODOs)", projectsWithNoIncompletes, projectPriority, projectPercentages)
|
||||
print("")
|
||||
print("* Projects and contexts with an asterisk next to them denote prioritized tasks.")
|
||||
print("Project with prioritized tasks are listed first, then sorted by number of open todos.")
|
||||
print("")
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
199
.todo.actions.d/weeklyreview.py
Normal file
199
.todo.actions.d/weeklyreview.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
""" TODO.TXT Weekly Review
|
||||
USAGE:
|
||||
weeklyreview.py [todo.txt] [done.txt] [projects.txt]
|
||||
|
||||
USAGE NOTES:
|
||||
Expects three text files as parameters:
|
||||
1 & 2. Properly-formatted todo.txt and done.txt files.
|
||||
3. A projects.txt file which lists one project per line, and any number of #goals associated with it.
|
||||
|
||||
See more on todo.txt here:
|
||||
http://todotxt.com
|
||||
|
||||
OUTPUT:
|
||||
Displays a count of how many tasks were completed associated with a goal.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import datetime
|
||||
|
||||
__version__ = "1.2"
|
||||
__date__ = "2016/03/17"
|
||||
__updated__ = "2016/03/17"
|
||||
__author__ = "Gina Trapani (ginatrapani@gmail.com)"
|
||||
__copyright__ = "Copyright 2016, Gina Trapani"
|
||||
__license__ = "GPL"
|
||||
__history__ = """
|
||||
0.1 - WIP
|
||||
"""
|
||||
|
||||
def usage():
|
||||
print("USAGE: %s [todo.txt] [done.txt] [projects.txt]" % (sys.argv[0], ))
|
||||
|
||||
def separator(c, r=42):
|
||||
sep = ""
|
||||
sep = c * r
|
||||
print(sep)
|
||||
|
||||
def printTitle(text):
|
||||
print("")
|
||||
r = len(text)
|
||||
print(text)
|
||||
separator("=", r)
|
||||
|
||||
def printHeader(text):
|
||||
r = len(text)
|
||||
print("")
|
||||
print(text)
|
||||
separator("-", r)
|
||||
|
||||
def main(argv):
|
||||
# make sure you have all your args
|
||||
if len(argv) < 3:
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
goal_projects = getGoalProjects(argv)
|
||||
#print(goal_projects)
|
||||
|
||||
last_7_days = getLast7Days()
|
||||
#print(last_7_days)
|
||||
|
||||
last_7_days_of_completions = getLast7DaysOfCompletions(argv, last_7_days)
|
||||
#print(last_7_days_of_completions)
|
||||
|
||||
project_completions = getProjectCompletions(argv, last_7_days_of_completions)
|
||||
#print(project_completions)
|
||||
|
||||
goal_completions = getGoalCompletions(goal_projects, project_completions)
|
||||
# print(goal_completions)
|
||||
|
||||
# Print report: For each item in goal_projects, print the goal, the number of tasks completed,
|
||||
# then each project and the number of tasks completed
|
||||
printTitle("Weekly Review for the past 7 days")
|
||||
|
||||
goals_not_moved = []
|
||||
goals_moved = []
|
||||
for goal in goal_projects:
|
||||
total_done = 0
|
||||
if goal in goal_completions:
|
||||
total_done = len(goal_completions[goal])
|
||||
goal_header = goal + " - " + str(total_done) + " done"
|
||||
if total_done > 0:
|
||||
printHeader(goal_header)
|
||||
for project in goal_projects[goal]:
|
||||
if project in project_completions:
|
||||
print(project + " - " + str(len(project_completions[project])) + " done" )
|
||||
for task in project_completions[project]:
|
||||
print(" " + task.strip())
|
||||
goals_moved.append(goal)
|
||||
else:
|
||||
goals_not_moved.append(goal)
|
||||
|
||||
# Print a list of goals that had no movement
|
||||
if len(goals_not_moved) > 0:
|
||||
printTitle("Goals with no progress")
|
||||
for goal in goals_not_moved:
|
||||
print(goal)
|
||||
|
||||
# Print summary
|
||||
print("")
|
||||
summary = str(len(last_7_days_of_completions)) + " completed tasks moved " + str(len(goals_moved)) + " out of " + str(len(goal_projects)) + " goals forward."
|
||||
separator("-", len(summary))
|
||||
print(summary)
|
||||
separator("-", len(summary))
|
||||
|
||||
# Warnings
|
||||
crossCheckCompletedProjects(project_completions, goal_projects)
|
||||
|
||||
|
||||
# Return an array of goals with total tasks completed.
|
||||
def getGoalCompletions(goal_projects, project_completions):
|
||||
goal_completions = {}
|
||||
goals = goal_projects.keys()
|
||||
for goal in goal_projects:
|
||||
for project in project_completions:
|
||||
if project in goal_projects[goal]:
|
||||
if goal not in goal_completions:
|
||||
goal_completions[goal] = project_completions[project]
|
||||
else:
|
||||
goal_completions[goal] = goal_completions[goal] + project_completions[project]
|
||||
return goal_completions
|
||||
|
||||
# Return the goal/project list as an array of arrays goalProjects[goal] = projects[]
|
||||
def getGoalProjects(argv):
|
||||
try:
|
||||
goal_projects = {}
|
||||
f = open (argv[2], "r")
|
||||
for line in f:
|
||||
words = line.split()
|
||||
for word in words:
|
||||
# Project
|
||||
if word[0:1] == "+":
|
||||
current_project = word
|
||||
# Goal
|
||||
if word[0:1] == "#":
|
||||
if word not in goal_projects:
|
||||
goal_projects[word] = [current_project];
|
||||
else:
|
||||
goal_projects[word].append(current_project)
|
||||
f.close()
|
||||
return goal_projects
|
||||
except IOError:
|
||||
print("ERROR: The file named %s could not be read."% (argv[1], ))
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
# Get the last 7 days as an array of todo.txt-formatted dates.
|
||||
def getLast7Days():
|
||||
today = datetime.date.today()
|
||||
last7Days = []
|
||||
for d in range(8):
|
||||
day_this_week = today - datetime.timedelta(days=d)
|
||||
last7Days.append(day_this_week.strftime('%Y-%m-%d'))
|
||||
return last7Days
|
||||
|
||||
# Return last 7 days of completed tasks from done.txt
|
||||
def getLast7DaysOfCompletions(argv, last_7_days):
|
||||
try:
|
||||
last_7_days_of_completions = []
|
||||
f = open (argv[1], "r")
|
||||
for line in f:
|
||||
words = line.split()
|
||||
if len(words) > 2 and words[1] in last_7_days:
|
||||
last_7_days_of_completions.append(line)
|
||||
f.close()
|
||||
return last_7_days_of_completions
|
||||
except IOError:
|
||||
print("ERROR: The file named %s could not be read."% (argv[1], ))
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
# Return an array of projects with the total tasks completed.
|
||||
def getProjectCompletions(argv, last_7_days_of_completions):
|
||||
project_completions = {}
|
||||
for task in last_7_days_of_completions:
|
||||
words = task.split()
|
||||
for word in words:
|
||||
if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+":
|
||||
if word not in project_completions:
|
||||
project_completions[word] = [task]
|
||||
else:
|
||||
project_completions[word].append(task)
|
||||
return project_completions
|
||||
|
||||
def crossCheckCompletedProjects(project_completions, goal_projects):
|
||||
for project in project_completions:
|
||||
goal_in_project = False
|
||||
for goal in goal_projects:
|
||||
if project in goal_projects[goal]:
|
||||
goal_in_project = True
|
||||
if goal_in_project == False:
|
||||
print("WARNING: Project " + project + " not in goal.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
38
GEN-VERSION-FILE
Executable file
38
GEN-VERSION-FILE
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
# Based on git's GIT-VERSION-GEN.
|
||||
|
||||
VF=VERSION-FILE
|
||||
DEF_VER=v2.2
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
if test -d .git -o -f .git &&
|
||||
VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
|
||||
case "$VN" in
|
||||
*$LF*) (exit 1) ;;
|
||||
v[0-9]*)
|
||||
git update-index -q --refresh
|
||||
test -z "$(git diff-index --name-only HEAD --)" ||
|
||||
VN="$VN-dirty" ;;
|
||||
esac
|
||||
then
|
||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||
else
|
||||
VN="$DEF_VER"
|
||||
fi
|
||||
|
||||
VN=$(expr "$VN" : v*'\(.*\)')
|
||||
|
||||
if test -r $VF
|
||||
then
|
||||
VC=$(sed -e 's/^VERSION=//' <$VF)
|
||||
else
|
||||
VC=unset
|
||||
fi
|
||||
test "$VN" = "$VC" || {
|
||||
echo >&2 "VERSION=$VN"
|
||||
echo "VERSION=$VN" >$VF
|
||||
}
|
||||
|
||||
|
||||
52
Makefile
Normal file
52
Makefile
Normal file
@@ -0,0 +1,52 @@
|
||||
#
|
||||
# Makefile for todo.txt
|
||||
#
|
||||
|
||||
# Dynamically detect/generate version file as necessary
|
||||
# This file will define a variable called VERSION.
|
||||
.PHONY: .FORCE-VERSION-FILE
|
||||
VERSION-FILE: .FORCE-VERSION-FILE
|
||||
@./GEN-VERSION-FILE
|
||||
-include VERSION-FILE
|
||||
|
||||
# Maybe this will include the version in it.
|
||||
todo.sh: VERSION-FILE
|
||||
|
||||
# For packaging
|
||||
DISTFILES := todo.cfg
|
||||
|
||||
DISTNAME=todo.txt_cli-$(VERSION)
|
||||
dist: $(DISTFILES) todo.sh
|
||||
mkdir -p $(DISTNAME)
|
||||
cp -f $(DISTFILES) $(DISTNAME)/
|
||||
sed -e 's/@DEV_VERSION@/'$(VERSION)'/' todo.sh > $(DISTNAME)/todo.sh
|
||||
tar cf $(DISTNAME).tar $(DISTNAME)/
|
||||
gzip -f -9 $(DISTNAME).tar
|
||||
zip -9r $(DISTNAME).zip $(DISTNAME)/
|
||||
rm -r $(DISTNAME)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(DISTNAME).tar.gz $(DISTNAME).zip
|
||||
|
||||
|
||||
#
|
||||
# Testing
|
||||
#
|
||||
TESTS = $(wildcard tests/t[0-9][0-9][0-9][0-9]-*.sh)
|
||||
#TEST_OPTIONS=--verbose
|
||||
|
||||
test-pre-clean:
|
||||
rm -rf tests/test-results "tests/trash directory"*
|
||||
|
||||
aggregate-results: $(TESTS)
|
||||
|
||||
$(TESTS): test-pre-clean
|
||||
-cd tests && sh $(notdir $@) $(TEST_OPTIONS)
|
||||
|
||||
test: aggregate-results
|
||||
tests/aggregate-results.sh tests/test-results/t*-*
|
||||
rm -rf tests/test-results
|
||||
|
||||
# Force tests to get run every time
|
||||
.PHONY: test test-pre-clean aggregate-results $(TESTS)
|
||||
111
README
111
README
@@ -1,111 +0,0 @@
|
||||
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
|
||||
|
||||
Actions:
|
||||
add "THING I NEED TO DO +project @context"
|
||||
a "THING I NEED TO DO +project @context"
|
||||
Adds THING I NEED TO DO to your todo.txt file on its own line.
|
||||
Project and context notation optional.
|
||||
Quotes optional.
|
||||
|
||||
addto DEST "TEXT TO ADD"
|
||||
Adds a line of text to any file located in the todo.txt directory.
|
||||
For example, addto inbox.txt "decide about vacation"
|
||||
|
||||
append NUMBER "TEXT TO APPEND"
|
||||
app NUMBER "TEXT TO APPEND"
|
||||
Adds TEXT TO APPEND to the end of the todo on line NUMBER.
|
||||
Quotes optional.
|
||||
|
||||
archive
|
||||
Moves done items from todo.txt to done.txt and removes blank lines.
|
||||
|
||||
del NUMBER [TERM]
|
||||
rm NUMBER [TERM]
|
||||
Deletes the item on line NUMBER in todo.txt.
|
||||
If term specified, deletes only the term from the line.
|
||||
|
||||
depri NUMBER
|
||||
dp NUMBER
|
||||
Deprioritizes (removes the priority) from the item
|
||||
on line NUMBER in todo.txt.
|
||||
|
||||
do NUMBER
|
||||
Marks item on line NUMBER as done in todo.txt.
|
||||
|
||||
list [TERM...]
|
||||
ls [TERM...]
|
||||
Displays all todo's that contain TERM(s) sorted by priority with line
|
||||
numbers. If no TERM specified, lists entire todo.txt.
|
||||
|
||||
listall [TERM...]
|
||||
lsa [TERM...]
|
||||
Displays all the lines in todo.txt AND done.txt that contain TERM(s)
|
||||
sorted by priority with line numbers. If no TERM specified, lists
|
||||
entire todo.txt AND done.txt concatenated and sorted.
|
||||
|
||||
listcon
|
||||
lsc
|
||||
Lists all the task contexts that start with the @ sign in todo.txt.
|
||||
|
||||
listfile SRC [TERM...]
|
||||
lf SRC [TERM...]
|
||||
Displays all the lines in SRC file located in the todo.txt directory,
|
||||
sorted by priority with line numbers. If TERM specified, lists
|
||||
all lines that contain TERM in SRC file.
|
||||
|
||||
listpri [PRIORITY]
|
||||
lsp [PRIORITY]
|
||||
Displays all items prioritized PRIORITY.
|
||||
If no PRIORITY specified, lists all prioritized items.
|
||||
|
||||
listproj
|
||||
lsprj
|
||||
Lists all the projects that start with the + sign in todo.txt.
|
||||
|
||||
move NUMBER DEST [SRC]
|
||||
mv NUMBER DEST [SRC]
|
||||
Moves a line from source text file (SRC) to destination text file (DEST).
|
||||
Both source and destination file must be located in the directory defined
|
||||
in the configuration directory. When SRC is not defined
|
||||
it's by default todo.txt.
|
||||
|
||||
prepend NUMBER "TEXT TO PREPEND"
|
||||
prep NUMBER "TEXT TO PREPEND"
|
||||
Adds TEXT TO PREPEND to the beginning of the todo on line NUMBER.
|
||||
Quotes optional.
|
||||
|
||||
pri NUMBER PRIORITY
|
||||
p NUMBER PRIORITY
|
||||
Adds PRIORITY to todo on line NUMBER. If the item is already
|
||||
prioritized, replaces current priority with new PRIORITY.
|
||||
PRIORITY must be an uppercase letter between A and Z.
|
||||
|
||||
replace NUMBER "UPDATED TODO"
|
||||
Replaces todo on line NUMBER with UPDATED TODO.
|
||||
|
||||
report
|
||||
Adds the number of open todo's and closed done's to report.txt.
|
||||
|
||||
|
||||
|
||||
Options:
|
||||
-d CONFIG_FILE
|
||||
Use a configuration file other than the default ~/todo.cfg
|
||||
-f
|
||||
Forces actions without confirmation or interactive input
|
||||
-h
|
||||
Display this help message
|
||||
-p
|
||||
Plain mode turns off colors
|
||||
-a
|
||||
Don't auto-archive tasks automatically on completion
|
||||
-n
|
||||
Don't preserve line numbers; automatically remove blank lines
|
||||
on task deletion
|
||||
-t
|
||||
Prepend the current date to a task automatically
|
||||
when it's added.
|
||||
-v
|
||||
Verbose mode turns on confirmation messages
|
||||
-V
|
||||
Displays version, license and credits
|
||||
26
README.textile
Normal file
26
README.textile
Normal file
@@ -0,0 +1,26 @@
|
||||
h1. TODO.TXT Command Line Interface
|
||||
|
||||
A simple and extensible shell script for managing your todo.txt file.
|
||||
|
||||
h2. "Downloads":http://github.com/ginatrapani/todo.txt-cli/downloads
|
||||
|
||||
"Download the latest stable release":http://github.com/ginatrapani/todo.txt-cli/downloads for use on your desktop or server.
|
||||
|
||||
h2. "Documentation":http://wiki.github.com/ginatrapani/todo.txt-cli
|
||||
|
||||
* "User Documentation":http://wiki.github.com/ginatrapani/todo.txt-cli/user-documentation - Find out "how to install and use Todo.txt CLI":http://wiki.github.com/ginatrapani/todo.txt-cli/user-documentation, and get tips and tricks.
|
||||
|
||||
* "Developer Documentation":http://wiki.github.com/ginatrapani/todo.txt-cli/developer-documentation - "Contribute to Todo.txt CLI":http://wiki.github.com/ginatrapani/todo.txt-cli/developer-documentation and build your own custom add-ons.
|
||||
|
||||
h2. "Mailing List":http://groups.yahoo.com/group/todotxt/
|
||||
|
||||
Get support from users and developers on the "mailing list":http://groups.yahoo.com/group/todotxt/.
|
||||
|
||||
h2. Quick Links
|
||||
|
||||
* Original anemic release by "Gina Trapani":http://ginatrapani.org on 5/11/2006.
|
||||
* Raised to great heights by "brainy and dedicated volunteers":http://github.com/ginatrapani/todo.txt-cli/network.
|
||||
* Licensed under the "GPL":http://www.gnu.org/copyleft/gpl.html
|
||||
* "Add-on Directory":http://wiki.github.com/ginatrapani/todo.txt-cli/todosh-add-on-directory
|
||||
* "Changelog":http://wiki.github.com/ginatrapani/todo.txt-cli/todosh-changelog
|
||||
* "Known Bugs":http://wiki.github.com/ginatrapani/todo.txt-cli/known-bugs
|
||||
2
tests/Makefile
Normal file
2
tests/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
test:
|
||||
$(MAKE) -C .. test
|
||||
219
tests/README
Normal file
219
tests/README
Normal file
@@ -0,0 +1,219 @@
|
||||
todo.sh tests
|
||||
=============
|
||||
|
||||
This directory holds test scripts for todo.sh. The
|
||||
first part of this short document describes how to run the tests
|
||||
and read their output.
|
||||
|
||||
When fixing the tools or adding enhancements, you are strongly
|
||||
encouraged to add tests in this directory to cover what you are
|
||||
trying to fix or enhance. The later part of this short document
|
||||
describes how your test scripts should be organized.
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
The easiest way to run tests is to say "make test" from the top-level.
|
||||
This runs all the tests.
|
||||
|
||||
rm -rf tests/test-results "tests/trash directory"*
|
||||
cd tests && sh t0000-config.sh
|
||||
* ok 1: no config file
|
||||
* ok 2: config file (default location 1)
|
||||
* ok 3: config file (default location 2)
|
||||
* ok 4: config file (command line)
|
||||
* ok 5: config file (env variable)
|
||||
* passed all 5 test(s)
|
||||
cd tests && sh t0001-null.sh
|
||||
* ok 1: null ls
|
||||
* passed all 1 test(s)
|
||||
rm -rf tests/test-results
|
||||
|
||||
Or you can run each test individually from command line, like
|
||||
this:
|
||||
|
||||
$ ./t0001-null.sh
|
||||
* ok 1: null ls
|
||||
* passed all 1 test(s)
|
||||
|
||||
You can pass --verbose (or -v), --debug (or -d), and --immediate
|
||||
(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
|
||||
appropriately before running "make".
|
||||
|
||||
--verbose::
|
||||
This makes the test more verbose. Specifically, the
|
||||
command being run and their output if any are also
|
||||
output.
|
||||
|
||||
--debug::
|
||||
This may help the person who is developing a new test.
|
||||
It causes the command defined with test_debug to run.
|
||||
|
||||
--immediate::
|
||||
This causes the test to immediately exit upon the first
|
||||
failed test.
|
||||
|
||||
--long-tests::
|
||||
This causes additional long-running tests to be run (where
|
||||
available), for more exhaustive testing.
|
||||
|
||||
--tee::
|
||||
In addition to printing the test output to the terminal,
|
||||
write it to files named 't/test-results/$TEST_NAME.out'.
|
||||
As the names depend on the tests' file names, it is safe to
|
||||
run the tests with this option in parallel.
|
||||
|
||||
Skipping Tests
|
||||
--------------
|
||||
|
||||
In some environments, certain tests have no way of succeeding
|
||||
due to platform limitation, such as lack of 'unzip' program, or
|
||||
filesystem that do not allow arbitrary sequence of non-NUL bytes
|
||||
as pathnames.
|
||||
|
||||
You should be able to say something like
|
||||
|
||||
$ SKIP_TESTS=t0000.2 sh ./t0000-config.sh
|
||||
|
||||
and even:
|
||||
|
||||
$ SKIP_TESTS='t[0-4]??? t91?? t9200.8' make
|
||||
|
||||
to omit such tests. The value of the environment variable is a
|
||||
SP separated list of patterns that tells which tests to skip,
|
||||
and either can match the "t[0-9]{4}" part to skip the whole
|
||||
test, or t[0-9]{4} followed by ".$number" to say which
|
||||
particular test to skip.
|
||||
|
||||
Note that some tests in the existing test suite rely on previous
|
||||
test item, so you cannot arbitrarily disable one and expect the
|
||||
remainder of test to check what the test originally was intended
|
||||
to check.
|
||||
|
||||
|
||||
Naming Tests
|
||||
------------
|
||||
|
||||
The test files are named as:
|
||||
|
||||
tNNNN-commandname-details.sh
|
||||
|
||||
where N is a decimal digit.
|
||||
|
||||
First digit tells the family:
|
||||
|
||||
0 - the absolute basics and global stuff
|
||||
1 - basic every-day usage
|
||||
2 - add ins
|
||||
|
||||
Second digit tells the particular command we are testing.
|
||||
|
||||
Third digit (optionally) tells the particular switch or group of switches
|
||||
we are testing.
|
||||
|
||||
If you create files under tests/ directory (i.e. here) that is not
|
||||
the top-level test script, never name the file to match the above
|
||||
pattern. The Makefile here considers all such files as the
|
||||
top-level test script and tries to run all of them. A care is
|
||||
especially needed if you are creating a common test library
|
||||
file, similar to test-lib.sh, because such a library file may
|
||||
not be suitable for standalone execution.
|
||||
|
||||
|
||||
Writing Tests
|
||||
-------------
|
||||
|
||||
The test script is written as a shell script. It should start
|
||||
with the standard "#!/bin/sh" with copyright notices, and an
|
||||
assignment to variable 'test_description', like this:
|
||||
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
|
||||
test_description='xxx test (option --frotz)
|
||||
|
||||
This test registers the following structure in the cache
|
||||
and tries to run git-ls-files with option --frotz.'
|
||||
|
||||
|
||||
Source 'test-lib.sh'
|
||||
--------------------
|
||||
|
||||
After assigning test_description, the test script should source
|
||||
test-lib.sh like this:
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
This test harness library does the following things:
|
||||
|
||||
- If the script is invoked with command line argument --help
|
||||
(or -h), it shows the test_description and exits.
|
||||
|
||||
- Creates an empty test directory with an empty todo file
|
||||
database and chdir(2) into it. This directory is 't/trash directory'
|
||||
if you must know, but I do not think you care.
|
||||
|
||||
- Defines standard test helper functions for your scripts to
|
||||
use. These functions are designed to make all scripts behave
|
||||
consistently when command line arguments --verbose (or -v),
|
||||
--debug (or -d), and --immediate (or -i) is given.
|
||||
|
||||
|
||||
End with test_done
|
||||
------------------
|
||||
|
||||
Your script will be a sequence of tests, using helper functions
|
||||
from the test harness library. At the end of the script, call
|
||||
'test_done'.
|
||||
|
||||
|
||||
Test harness library
|
||||
--------------------
|
||||
|
||||
There are a handful helper functions defined in the test harness
|
||||
library for your script to use.
|
||||
|
||||
- test_expect_success <message> <script>
|
||||
|
||||
This takes two strings as parameter, and evaluates the
|
||||
<script>. If it yields success, test is considered
|
||||
successful. <message> should state what it is testing.
|
||||
|
||||
Example:
|
||||
|
||||
test_expect_success \
|
||||
'git-write-tree should be able to write an empty tree.' \
|
||||
'tree=$(git-write-tree)'
|
||||
|
||||
- test_expect_failure <message> <script>
|
||||
|
||||
This is NOT the opposite of test_expect_success, but is used
|
||||
to mark a test that demonstrates a known breakage. Unlike
|
||||
the usual test_expect_success tests, which say "ok" on
|
||||
success and "FAIL" on failure, this will say "FIXED" on
|
||||
success and "still broken" on failure. Failures from these
|
||||
tests won't cause -i (immediate) to stop.
|
||||
|
||||
- test_debug <script>
|
||||
|
||||
This takes a single argument, <script>, and evaluates it only
|
||||
when the test script is started with --debug command line
|
||||
argument. This is primarily meant for use during the
|
||||
development of a new test script.
|
||||
|
||||
- test_done
|
||||
|
||||
Your test script must have test_done at the end. Its purpose
|
||||
is to summarize successes and failures in the test script and
|
||||
exit with an appropriate error code.
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
This test framework was derived from the framework used by
|
||||
git itself, written originally by Junio Hamano and licensed
|
||||
for use under the GPL.
|
||||
34
tests/aggregate-results.sh
Executable file
34
tests/aggregate-results.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
fixed=0
|
||||
success=0
|
||||
failed=0
|
||||
broken=0
|
||||
total=0
|
||||
|
||||
for file
|
||||
do
|
||||
while read type value
|
||||
do
|
||||
case $type in
|
||||
'')
|
||||
continue ;;
|
||||
fixed)
|
||||
fixed=$(($fixed + $value)) ;;
|
||||
success)
|
||||
success=$(($success + $value)) ;;
|
||||
failed)
|
||||
failed=$(($failed + $value)) ;;
|
||||
broken)
|
||||
broken=$(($broken + $value)) ;;
|
||||
total)
|
||||
total=$(($total + $value)) ;;
|
||||
esac
|
||||
done <"$file"
|
||||
done
|
||||
|
||||
printf "%-8s%d\n" fixed $fixed
|
||||
printf "%-8s%d\n" success $success
|
||||
printf "%-8s%d\n" failed $failed
|
||||
printf "%-8s%d\n" broken $broken
|
||||
printf "%-8s%d\n" total $total
|
||||
61
tests/t0000-config.sh
Executable file
61
tests/t0000-config.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='todo.sh configuration file location
|
||||
|
||||
This test just makes sure that todo.sh can find its
|
||||
config files in the default locations and take arguments
|
||||
to find it somewhere else.
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
# Remove the pre-created todo.cfg to test behavior in its absence
|
||||
rm -f todo.cfg
|
||||
echo "Fatal error: Cannot read configuration file $HOME/todo.cfg" > expect
|
||||
test_expect_success 'no config file' '
|
||||
todo.sh > output 2>&1 || test_cmp expect output
|
||||
'
|
||||
|
||||
# All the below tests will output the usage message.
|
||||
cat > expect << EOF
|
||||
Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description]
|
||||
Try 'todo.sh -h' for more information.
|
||||
EOF
|
||||
|
||||
cat > test.cfg << EOF
|
||||
export TODO_DIR=.
|
||||
export TODO_FILE="$TODO_DIR/todo.txt"
|
||||
export DONE_FILE="$TODO_DIR/done.txt"
|
||||
export REPORT_FILE="$TODO_DIR/report.txt"
|
||||
export TMP_FILE="$TODO_DIR/todo.tmp"
|
||||
touch used_config
|
||||
EOF
|
||||
|
||||
rm -f used_config
|
||||
test_expect_success 'config file (default location 1)' '
|
||||
cp test.cfg todo.cfg
|
||||
todo.sh > output;
|
||||
test_cmp expect output && test -f used_config &&
|
||||
rm -f todo.cfg
|
||||
'
|
||||
|
||||
rm -f used_config
|
||||
test_expect_success 'config file (default location 2)' '
|
||||
cp test.cfg .todo.cfg
|
||||
todo.sh > output;
|
||||
test_cmp expect output && test -f used_config &&
|
||||
rm -f .todo.cfg
|
||||
'
|
||||
|
||||
rm -f used_config
|
||||
test_expect_success 'config file (command line)' '
|
||||
todo.sh -d test.cfg > output;
|
||||
test_cmp expect output && test -f used_config
|
||||
'
|
||||
|
||||
rm -f used_config
|
||||
test_expect_success 'config file (env variable)' '
|
||||
TODOTXT_CFG_FILE=test.cfg todo.sh > output;
|
||||
test_cmp expect output && test -f used_config
|
||||
'
|
||||
|
||||
test_done
|
||||
101
tests/t0001-null.sh
Executable file
101
tests/t0001-null.sh
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='todo.sh basic null functionality test.
|
||||
|
||||
This test just makes sure the basic commands work,
|
||||
when there are no todos.
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
#
|
||||
# ls|list
|
||||
#
|
||||
cat > expect <<EOF
|
||||
--
|
||||
TODO: 0 of 0 tasks shown from $HOME/todo.txt
|
||||
EOF
|
||||
|
||||
test_expect_success 'null ls' '
|
||||
todo.sh ls > output && test_cmp expect output
|
||||
'
|
||||
test_expect_success 'null list' '
|
||||
todo.sh list > output && test_cmp expect output
|
||||
'
|
||||
test_expect_success 'null list filter' '
|
||||
todo.sh list filter > output && test_cmp expect output
|
||||
'
|
||||
|
||||
#
|
||||
# lsp|listpri
|
||||
#
|
||||
# Re-use expect from ls.
|
||||
test_expect_success 'null lsp' '
|
||||
todo.sh lsp > output && test_cmp expect output
|
||||
'
|
||||
test_expect_success 'null listpri' '
|
||||
todo.sh listpri > output && test_cmp expect output
|
||||
'
|
||||
test_expect_success 'null listpri a' '
|
||||
todo.sh listpri a > output && test_cmp expect output
|
||||
'
|
||||
|
||||
#
|
||||
# lsa|listall
|
||||
#
|
||||
cat > expect <<EOF
|
||||
--
|
||||
TODO: 0 of 0 tasks shown from $HOME/todo.tmp
|
||||
EOF
|
||||
|
||||
test_expect_success 'null lsa' '
|
||||
todo.sh lsa > output && test_cmp expect output
|
||||
'
|
||||
test_expect_success 'null list' '
|
||||
todo.sh listall > output && test_cmp expect output
|
||||
'
|
||||
test_expect_success 'null list filter' '
|
||||
todo.sh listall filter > output && test_cmp expect output
|
||||
'
|
||||
|
||||
|
||||
#
|
||||
# lsc|listcon
|
||||
#
|
||||
test_expect_success 'null lsc' '
|
||||
todo.sh lsc > output && ! test -s output
|
||||
'
|
||||
test_expect_success 'null listcon' '
|
||||
todo.sh listcon > output && ! test -s output
|
||||
'
|
||||
|
||||
#
|
||||
# lsprj|listproj
|
||||
#
|
||||
test_expect_success 'null lsprj' '
|
||||
todo.sh lsprj > output && ! test -s output
|
||||
'
|
||||
test_expect_success 'null listproj' '
|
||||
todo.sh listproj > output && ! test -s output
|
||||
'
|
||||
|
||||
#
|
||||
# lf|listfile
|
||||
#
|
||||
cat > expect <<EOF
|
||||
TODO: File does not exist.
|
||||
EOF
|
||||
# XXX really should give a better usage error message here.
|
||||
test_expect_success 'null lf' '
|
||||
todo.sh lf > output || test_cmp expect output
|
||||
'
|
||||
test_expect_success 'null listfile' '
|
||||
todo.sh listfile > output || test_cmp expect output
|
||||
'
|
||||
cat > expect <<EOF
|
||||
TODO: File foo.txt does not exist.
|
||||
EOF
|
||||
test_expect_success 'null listfile foo.txt' '
|
||||
todo.sh listfile foo.txt > output || test_cmp expect output
|
||||
'
|
||||
|
||||
test_done
|
||||
467
tests/test-lib.sh
Normal file
467
tests/test-lib.sh
Normal file
@@ -0,0 +1,467 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
|
||||
# if --tee was passed, write the output not only to the terminal, but
|
||||
# additionally to the file test-results/$BASENAME.out, too.
|
||||
case "$TEST_TEE_STARTED, $* " in
|
||||
done,*)
|
||||
# do not redirect again
|
||||
;;
|
||||
*' --tee '*|*' --va'*)
|
||||
mkdir -p test-results
|
||||
BASE=test-results/$(basename "$0" .sh)
|
||||
(TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
|
||||
echo $? > $BASE.exit) | tee $BASE.out
|
||||
test "$(cat $BASE.exit)" = 0
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
# Keep the original TERM for say_color
|
||||
ORIGINAL_TERM=$TERM
|
||||
|
||||
# For repeatability, reset the environment to known value.
|
||||
LANG=C
|
||||
LC_ALL=C
|
||||
PAGER=cat
|
||||
TZ=UTC
|
||||
TERM=dumb
|
||||
export LANG LC_ALL PAGER TERM TZ
|
||||
EDITOR=:
|
||||
VISUAL=:
|
||||
|
||||
# Protect ourselves from common misconfiguration to export
|
||||
# CDPATH into the environment
|
||||
unset CDPATH
|
||||
|
||||
# Protect ourselves from using predefined TODOTXT_CFG_FILE
|
||||
unset TODOTXT_CFG_FILE
|
||||
|
||||
# Each test should start with something like this, after copyright notices:
|
||||
#
|
||||
# test_description='Description of this test...
|
||||
# This test checks if command xyzzy does the right thing...
|
||||
# '
|
||||
# . ./test-lib.sh
|
||||
[ "x$ORIGINAL_TERM" != "xdumb" ] && (
|
||||
TERM=$ORIGINAL_TERM &&
|
||||
export TERM &&
|
||||
[ -t 1 ] &&
|
||||
tput bold >/dev/null 2>&1 &&
|
||||
tput setaf 1 >/dev/null 2>&1 &&
|
||||
tput sgr0 >/dev/null 2>&1
|
||||
) &&
|
||||
color=t
|
||||
|
||||
while test "$#" -ne 0
|
||||
do
|
||||
case "$1" in
|
||||
-d|--d|--de|--deb|--debu|--debug)
|
||||
debug=t; shift ;;
|
||||
-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
|
||||
immediate=t; shift ;;
|
||||
-l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
|
||||
TODOTXT_TEST_LONG=t; export TODOTXT_TEST_LONG; shift ;;
|
||||
-h|--h|--he|--hel|--help)
|
||||
help=t; shift ;;
|
||||
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
|
||||
verbose=t; shift ;;
|
||||
-q|--q|--qu|--qui|--quie|--quiet)
|
||||
quiet=t; shift ;;
|
||||
--no-color)
|
||||
color=; shift ;;
|
||||
--no-python)
|
||||
# noop now...
|
||||
shift ;;
|
||||
--tee)
|
||||
shift ;; # was handled already
|
||||
*)
|
||||
break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if test -n "$color"; then
|
||||
say_color () {
|
||||
(
|
||||
TERM=$ORIGINAL_TERM
|
||||
export TERM
|
||||
case "$1" in
|
||||
error) tput bold; tput setaf 1;; # bold red
|
||||
skip) tput bold; tput setaf 2;; # bold green
|
||||
pass) tput setaf 2;; # green
|
||||
info) tput setaf 3;; # brown
|
||||
*) test -n "$quiet" && return;;
|
||||
esac
|
||||
shift
|
||||
printf "* %s" "$*"
|
||||
tput sgr0
|
||||
echo
|
||||
)
|
||||
}
|
||||
else
|
||||
say_color() {
|
||||
test -z "$1" && test -n "$quiet" && return
|
||||
shift
|
||||
echo "* $*"
|
||||
}
|
||||
fi
|
||||
|
||||
error () {
|
||||
say_color error "error: $*"
|
||||
trap - EXIT
|
||||
exit 1
|
||||
}
|
||||
|
||||
say () {
|
||||
say_color info "$*"
|
||||
}
|
||||
|
||||
test "${test_description}" != "" ||
|
||||
error "Test script did not set test_description."
|
||||
|
||||
if test "$help" = "t"
|
||||
then
|
||||
echo "$test_description"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec 5>&1
|
||||
if test "$verbose" = "t"
|
||||
then
|
||||
exec 4>&2 3>&1
|
||||
else
|
||||
exec 4>/dev/null 3>/dev/null
|
||||
fi
|
||||
|
||||
test_failure=0
|
||||
test_count=0
|
||||
test_fixed=0
|
||||
test_broken=0
|
||||
test_success=0
|
||||
|
||||
die () {
|
||||
echo >&5 "FATAL: Unexpected exit with code $?"
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap 'die' EXIT
|
||||
|
||||
# The semantics of the editor variables are that of invoking
|
||||
# sh -c "$EDITOR \"$@\"" files ...
|
||||
#
|
||||
# If our trash directory contains shell metacharacters, they will be
|
||||
# interpreted if we just set $EDITOR directly, so do a little dance with
|
||||
# environment variables to work around this.
|
||||
#
|
||||
# In particular, quoting isn't enough, as the path may contain the same quote
|
||||
# that we're using.
|
||||
test_set_editor () {
|
||||
FAKE_EDITOR="$1"
|
||||
export FAKE_EDITOR
|
||||
VISUAL='"$FAKE_EDITOR"'
|
||||
export VISUAL
|
||||
}
|
||||
|
||||
# You are not expected to call test_ok_ and test_failure_ directly, use
|
||||
# the text_expect_* functions instead.
|
||||
|
||||
test_ok_ () {
|
||||
test_success=$(($test_success + 1))
|
||||
say_color "" " ok $test_count: $@"
|
||||
}
|
||||
|
||||
test_failure_ () {
|
||||
test_failure=$(($test_failure + 1))
|
||||
say_color error "FAIL $test_count: $1"
|
||||
shift
|
||||
echo "$@" | sed -e 's/^/ /'
|
||||
test "$immediate" = "" || { trap - EXIT; exit 1; }
|
||||
}
|
||||
|
||||
test_known_broken_ok_ () {
|
||||
test_fixed=$(($test_fixed+1))
|
||||
say_color "" " FIXED $test_count: $@"
|
||||
}
|
||||
|
||||
test_known_broken_failure_ () {
|
||||
test_broken=$(($test_broken+1))
|
||||
say_color skip " still broken $test_count: $@"
|
||||
}
|
||||
|
||||
test_debug () {
|
||||
test "$debug" = "" || eval "$1"
|
||||
}
|
||||
|
||||
test_run_ () {
|
||||
eval >&3 2>&4 "$1"
|
||||
eval_ret="$?"
|
||||
return 0
|
||||
}
|
||||
|
||||
test_skip () {
|
||||
test_count=$(($test_count+1))
|
||||
to_skip=
|
||||
for skp in $SKIP_TESTS
|
||||
do
|
||||
case $this_test.$test_count in
|
||||
$skp)
|
||||
to_skip=t
|
||||
esac
|
||||
done
|
||||
case "$to_skip" in
|
||||
t)
|
||||
say_color skip >&3 "skipping test: $@"
|
||||
say_color skip "skip $test_count: $1"
|
||||
: true
|
||||
;;
|
||||
*)
|
||||
false
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
test_expect_failure () {
|
||||
test "$#" = 2 ||
|
||||
error "bug in the test script: not 2 parameters to test-expect-failure"
|
||||
if ! test_skip "$@"
|
||||
then
|
||||
say >&3 "checking known breakage: $2"
|
||||
test_run_ "$2"
|
||||
if [ "$?" = 0 -a "$eval_ret" = 0 ]
|
||||
then
|
||||
test_known_broken_ok_ "$1"
|
||||
else
|
||||
test_known_broken_failure_ "$1"
|
||||
fi
|
||||
fi
|
||||
echo >&3 ""
|
||||
}
|
||||
|
||||
test_expect_success () {
|
||||
test "$#" = 2 ||
|
||||
error "bug in the test script: not 2 parameters to test-expect-success"
|
||||
if ! test_skip "$@"
|
||||
then
|
||||
say >&3 "expecting success: $2"
|
||||
test_run_ "$2"
|
||||
if [ "$?" = 0 -a "$eval_ret" = 0 ]
|
||||
then
|
||||
test_ok_ "$1"
|
||||
else
|
||||
test_failure_ "$@"
|
||||
fi
|
||||
fi
|
||||
echo >&3 ""
|
||||
}
|
||||
|
||||
test_expect_code () {
|
||||
test "$#" = 3 ||
|
||||
error "bug in the test script: not 3 parameters to test-expect-code"
|
||||
if ! test_skip "$@"
|
||||
then
|
||||
say >&3 "expecting exit code $1: $3"
|
||||
test_run_ "$3"
|
||||
if [ "$?" = 0 -a "$eval_ret" = "$1" ]
|
||||
then
|
||||
test_ok_ "$2"
|
||||
else
|
||||
test_failure_ "$@"
|
||||
fi
|
||||
fi
|
||||
echo >&3 ""
|
||||
}
|
||||
|
||||
# test_external runs external test scripts that provide continuous
|
||||
# test output about their progress, and succeeds/fails on
|
||||
# zero/non-zero exit code. It outputs the test output on stdout even
|
||||
# in non-verbose mode, and announces the external script with "* run
|
||||
# <n>: ..." before running it. When providing relative paths, keep in
|
||||
# mind that all scripts run in "trash directory".
|
||||
# Usage: test_external description command arguments...
|
||||
# Example: test_external 'Perl API' perl ../path/to/test.pl
|
||||
test_external () {
|
||||
test "$#" -eq 3 ||
|
||||
error >&5 "bug in the test script: not 3 parameters to test_external"
|
||||
descr="$1"
|
||||
shift
|
||||
if ! test_skip "$descr" "$@"
|
||||
then
|
||||
# Announce the script to reduce confusion about the
|
||||
# test output that follows.
|
||||
say_color "" " run $test_count: $descr ($*)"
|
||||
# Run command; redirect its stderr to &4 as in
|
||||
# test_run_, but keep its stdout on our stdout even in
|
||||
# non-verbose mode.
|
||||
"$@" 2>&4
|
||||
if [ "$?" = 0 ]
|
||||
then
|
||||
test_ok_ "$descr"
|
||||
else
|
||||
test_failure_ "$descr" "$@"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Like test_external, but in addition tests that the command generated
|
||||
# no output on stderr.
|
||||
test_external_without_stderr () {
|
||||
# The temporary file has no (and must have no) security
|
||||
# implications.
|
||||
tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
|
||||
stderr="$tmp/todotxt-external-stderr.$$.tmp"
|
||||
test_external "$@" 4> "$stderr"
|
||||
[ -f "$stderr" ] || error "Internal error: $stderr disappeared."
|
||||
descr="no stderr: $1"
|
||||
shift
|
||||
say >&3 "expecting no stderr from previous command"
|
||||
if [ ! -s "$stderr" ]; then
|
||||
rm "$stderr"
|
||||
test_ok_ "$descr"
|
||||
else
|
||||
if [ "$verbose" = t ]; then
|
||||
output=`echo; echo Stderr is:; cat "$stderr"`
|
||||
else
|
||||
output=
|
||||
fi
|
||||
# rm first in case test_failure exits.
|
||||
rm "$stderr"
|
||||
test_failure_ "$descr" "$@" "$output"
|
||||
fi
|
||||
}
|
||||
|
||||
# This is not among top-level (test_expect_success | test_expect_failure)
|
||||
# but is a prefix that can be used in the test script, like:
|
||||
#
|
||||
# test_expect_success 'complain and die' '
|
||||
# do something &&
|
||||
# do something else &&
|
||||
# test_must_fail git checkout ../outerspace
|
||||
# '
|
||||
#
|
||||
# Writing this as "! git checkout ../outerspace" is wrong, because
|
||||
# the failure could be due to a segv. We want a controlled failure.
|
||||
|
||||
test_must_fail () {
|
||||
"$@"
|
||||
test $? -gt 0 -a $? -le 129 -o $? -gt 192
|
||||
}
|
||||
|
||||
# test_cmp is a helper function to compare actual and expected output.
|
||||
# You can use it like:
|
||||
#
|
||||
# test_expect_success 'foo works' '
|
||||
# echo expected >expected &&
|
||||
# foo >actual &&
|
||||
# test_cmp expected actual
|
||||
# '
|
||||
#
|
||||
# This could be written as either "cmp" or "diff -u", but:
|
||||
# - cmp's output is not nearly as easy to read as diff -u
|
||||
# - not all diff versions understand "-u"
|
||||
|
||||
test_cmp() {
|
||||
diff -u "$@"
|
||||
}
|
||||
|
||||
test_done () {
|
||||
trap - EXIT
|
||||
test_results_dir="$TEST_DIRECTORY/test-results"
|
||||
mkdir -p "$test_results_dir"
|
||||
test_results_path="$test_results_dir/${0%.sh}-$$"
|
||||
|
||||
echo "total $test_count" >> $test_results_path
|
||||
echo "success $test_success" >> $test_results_path
|
||||
echo "fixed $test_fixed" >> $test_results_path
|
||||
echo "broken $test_broken" >> $test_results_path
|
||||
echo "failed $test_failure" >> $test_results_path
|
||||
echo "" >> $test_results_path
|
||||
|
||||
if test "$test_fixed" != 0
|
||||
then
|
||||
say_color pass "fixed $test_fixed known breakage(s)"
|
||||
fi
|
||||
if test "$test_broken" != 0
|
||||
then
|
||||
say_color error "still have $test_broken known breakage(s)"
|
||||
msg="remaining $(($test_count-$test_broken)) test(s)"
|
||||
else
|
||||
msg="$test_count test(s)"
|
||||
fi
|
||||
case "$test_failure" in
|
||||
0)
|
||||
say_color pass "passed all $msg"
|
||||
|
||||
# Clean up this test.
|
||||
test -d "$remove_trash" &&
|
||||
cd "$(dirname "$remove_trash")" &&
|
||||
rm -rf "$(basename "$remove_trash")"
|
||||
|
||||
exit 0 ;;
|
||||
|
||||
*)
|
||||
say_color error "failed $test_failure among $msg"
|
||||
exit 1 ;;
|
||||
|
||||
esac
|
||||
}
|
||||
|
||||
# Make sure we are testing the latest version.
|
||||
TEST_DIRECTORY=$(pwd)
|
||||
PATH=$TEST_DIRECTORY/..:$PATH
|
||||
|
||||
# Test repository
|
||||
test="trash directory.$(basename "$0" .sh)"
|
||||
test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
|
||||
rm -fr "$test" || {
|
||||
trap - EXIT
|
||||
echo >&5 "FATAL: Cannot prepare test area"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Most tests can use the created repository, but some may need to create more.
|
||||
# Usage: test_init_todo <directory>
|
||||
test_init_todo () {
|
||||
test "$#" = 1 ||
|
||||
error "bug in the test script: not 1 parameter to test_init_todo"
|
||||
owd=`pwd`
|
||||
root="$1"
|
||||
mkdir -p "$root"
|
||||
cd "$root" || error "Cannot setup todo dir in $root"
|
||||
# Initialize the configuration file. Carefully quoted.
|
||||
sed -e 's|TODO_DIR=.*$|TODO_DIR="'"$TEST_DIRECTORY/$test"'"|' $TEST_DIRECTORY/../todo.cfg > todo.cfg
|
||||
cd "$owd"
|
||||
}
|
||||
|
||||
|
||||
test_init_todo "$test"
|
||||
# Use -P to resolve symlinks in our working directory so that the cwd
|
||||
# in subprocesses equals our $PWD (for pathname comparisons).
|
||||
cd -P "$test" || exit 1
|
||||
|
||||
# Since todo.sh refers to the home directory often,
|
||||
# make sure we don't accidentally grab the tester's config
|
||||
# but use something specified by the framework.
|
||||
HOME=$(pwd)
|
||||
export HOME
|
||||
|
||||
this_test=${0##*/}
|
||||
this_test=${this_test%%-*}
|
||||
for skp in $SKIP_TESTS
|
||||
do
|
||||
to_skip=
|
||||
for skp in $SKIP_TESTS
|
||||
do
|
||||
case "$this_test" in
|
||||
$skp)
|
||||
to_skip=t
|
||||
esac
|
||||
done
|
||||
case "$to_skip" in
|
||||
t)
|
||||
say_color skip >&3 "skipping test $this_test altogether"
|
||||
say_color skip "skip all tests in $this_test"
|
||||
test_done
|
||||
esac
|
||||
done
|
||||
70
todo.cfg
70
todo.cfg
@@ -1,41 +1,55 @@
|
||||
# === EDIT FILE LOCATIONS BELOW ===
|
||||
|
||||
# Your todo.txt directory
|
||||
#TODO_DIR="/Users/gina/Documents/todo"
|
||||
TODO_DIR="C:/Documents and Settings/gina/My Documents"
|
||||
#export TODO_DIR="/Users/gina/Documents/todo"
|
||||
export TODO_DIR="C:/Documents and Settings/gina/My Documents"
|
||||
|
||||
# Your todo/done/report.txt locations
|
||||
TODO_FILE="$TODO_DIR/todo.txt"
|
||||
DONE_FILE="$TODO_DIR/done.txt"
|
||||
REPORT_FILE="$TODO_DIR/report.txt"
|
||||
TMP_FILE="$TODO_DIR/todo.tmp"
|
||||
export TODO_FILE="$TODO_DIR/todo.txt"
|
||||
export DONE_FILE="$TODO_DIR/done.txt"
|
||||
export REPORT_FILE="$TODO_DIR/report.txt"
|
||||
export TMP_FILE="$TODO_DIR/todo.tmp"
|
||||
|
||||
# You can customize your actions directory location
|
||||
#export TODO_ACTIONS_DIR="$HOME/.todo.actions.d"
|
||||
|
||||
# == EDIT FILE LOCATIONS ABOVE ===
|
||||
|
||||
# === COLOR MAP ===
|
||||
|
||||
NONE=''
|
||||
BLACK='\\033[0;30m'
|
||||
RED='\\033[0;31m'
|
||||
GREEN='\\033[0;32m'
|
||||
BROWN='\\033[0;33m'
|
||||
BLUE='\\033[0;34m'
|
||||
PURPLE='\\033[0;35m'
|
||||
CYAN='\\033[0;36m'
|
||||
LIGHT_GREY='\\033[0;37m'
|
||||
DARK_GREY='\\033[1;30m'
|
||||
LIGHT_RED='\\033[1;31m'
|
||||
LIGHT_GREEN='\\033[1;32m'
|
||||
YELLOW='\\033[1;33m'
|
||||
LIGHT_BLUE='\\033[1;34m'
|
||||
LIGHT_PURPLE='\\033[1;35m'
|
||||
LIGHT_CYAN='\\033[1;36m'
|
||||
WHITE='\\033[1;37m'
|
||||
DEFAULT='\\033[0m'
|
||||
## If you have re-mapped your color codes, you may need to
|
||||
## over-ride by uncommenting and editing these defaults.
|
||||
|
||||
# export BLACK='\\033[0;30m'
|
||||
# export RED='\\033[0;31m'
|
||||
# export GREEN='\\033[0;32m'
|
||||
# export BROWN='\\033[0;33m'
|
||||
# export BLUE='\\033[0;34m'
|
||||
# export PURPLE='\\033[0;35m'
|
||||
# export CYAN='\\033[0;36m'
|
||||
# export LIGHT_GREY='\\033[0;37m'
|
||||
# export DARK_GREY='\\033[1;30m'
|
||||
# export LIGHT_RED='\\033[1;31m'
|
||||
# export LIGHT_GREEN='\\033[1;32m'
|
||||
# export YELLOW='\\033[1;33m'
|
||||
# export LIGHT_BLUE='\\033[1;34m'
|
||||
# export LIGHT_PURPLE='\\033[1;35m'
|
||||
# export LIGHT_CYAN='\\033[1;36m'
|
||||
# export WHITE='\\033[1;37m'
|
||||
# export DEFAULT='\\033[0m'
|
||||
|
||||
# === PRIORITY COLORS ===
|
||||
|
||||
PRI_A=$YELLOW # color for A priority
|
||||
PRI_B=$GREEN # color for B priority
|
||||
PRI_C=$LIGHT_BLUE # color for C priority
|
||||
PRI_X=$WHITE # color for rest of them
|
||||
## Priorities can be any upper-case letter.
|
||||
## Colors are supported for the first three.
|
||||
## Uncomment and edit to override these defaults.
|
||||
|
||||
# export PRI_A=$YELLOW # color for A priority
|
||||
# export PRI_B=$GREEN # color for B priority
|
||||
# export PRI_C=$LIGHT_BLUE # color for C priority
|
||||
# export PRI_X=$WHITE # color for rest of them
|
||||
|
||||
# === BEHAVIOR ===
|
||||
|
||||
## customize list output
|
||||
# export TODOTXT_SORT_COMMAND='env LC_COLLATE=C sort -f -k2'
|
||||
|
||||
Reference in New Issue
Block a user