From: alex Date: Thu, 23 Sep 2021 18:47:36 +0000 (-0700) Subject: minor refactor X-Git-Tag: v1.1.0^0 X-Git-Url: http://git.infiniteadaptability.org/?a=commitdiff_plain;h=8a8d7c9f2666ce077adb42d8809df2f24d0836d5;p=workouts minor refactor updated autoconf/automake flags changed workouts.db location to be dependent on current directory, --homedir now changes directory add const qualifiers to functions where appropriate refactored ls added --name-only option to workouts ls (added for bash completion functionality) fixed existing ls tests; added more tests to cover --last-done and --name-only created bash completion script and added to automake --- diff --git a/.gitignore b/.gitignore index 70f6825..93255bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ workouts +!bash-completion/workouts workouts.db workouts*/ workouts*.tar.gz diff --git a/Makefile.am b/Makefile.am index 6498bf3..d556a2b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ AM_CPPFLAGS = \ - -I$(top_builddir)/include/ \ - -I$(top_srcdir)/include/ + -Wall \ + -Werror bin_PROGRAMS = workouts workouts_SOURCES = \ @@ -16,6 +16,7 @@ workouts_SOURCES = \ src/ls/head.c \ src/ls/last.c \ src/ls/ls.c \ + src/ls/name.c \ src/ls/single.c \ src/ls/recent.c \ src/ls/workout.c \ @@ -42,4 +43,7 @@ workouts_SOURCES += \ include/update.h \ include/usage.h -SUBDIRS = . test/integration test/unit +bashcompletiondir = $(datadir)/bash-completion/completions +dist_bashcompletion_DATA = bash-completion/workouts + +SUBDIRS = . test/integration test/unit diff --git a/bash-completion/workouts b/bash-completion/workouts new file mode 100644 index 0000000..6f15ea1 --- /dev/null +++ b/bash-completion/workouts @@ -0,0 +1,126 @@ +_workouts() { + IFS=$'\n' + COMPREPLY=() + local commands=$'add\nls\nrm\ntoggle\nupdate' + + declare -A options + options[--attribute]="--attribute" + options[--attr]="--attr" + options[-a]="-a" + options[--help]="--help" + options[--homedir]="--homedir" + options[-d]="-d" + options[--quiet]="--quiet" + options[--recent]="--recent" + options[-l]="-l" + options[--rows]="--rows" + options[-r]="-r" + options[--verbose]="--verbose" + options[-v]="-v" + options[--workout]="--workout" + options[-w]="-w" + + local ls_options=$'--filter\n-f\n--last-done\n--name-only' + + local type="default" + local utility="workouts" + + # search for type (only bother with latest; rest will be discarded) + for arg in "${COMP_WORDS[@]}"; do + case "$arg" in + --attribute|--attr|-a) type="attribute" ;; + --recent|-l) type="recent" ;; + --workout|-w) type="workout" ;; + add) utility="add" ;; + ls) utility="ls" ;; + rm) utility="rm" ;; + toggle) utility="toggle" ;; + update) utility="update" ;; + esac + unset options[$arg] + done + + opts= + for val in "${options[@]}"; do + opts+="$val" + opts+=$'\n' + done + + case "$3" in + add) + # don't need completions for attributes + if [[ "$type" == "recent" ]]; then + COMPREPLY+=($(workouts -w ls --name-only | grep "$2")) + fi + # don't need completions for workouts + ;; + --homedir|-d) + compopt -o nospace + COMPREPLY+=($(compgen -o dirnames -- "$2")) + ;; + --filter|-f) + local count=($(workouts -a ls | wc -l)) + local template + for (( i = 0; i< "$count"; i++ )); do + template+="x" + done + COMPREPLY+=("$template") + ;; + ls) + case "$type" in + # don't need completions for attributes + # don't need completions for recent + workout|default) + COMPREPLY+=($(compgen -W "${ls_options}" -- "$2")) + COMPREPLY+=($(workouts -w ls --name-only | grep -- "$2")) + ;; + esac + ;; + rm) + case "$type" in + attribute) + COMPREPLY+=($(workouts --attribute ls | grep "$2")) + ;; + recent|default) + COMPREPLY+=($(workouts -w ls --name-only | grep "$2")) + ;; + workout) + COMPREPLY+=($(workouts -w ls --name-only | grep "$2")) + ;; + esac + ;; + toggle) + case "$type" in + # don't need completions for attributes + # don't need completions for recent + workout|default) + COMPREPLY+=($(workouts -w ls --name-only | grep "$2")) + esac + ;; + update) + case "$type" in + attribute) + COMPREPLY+=($(workouts --attribute ls | grep "$2")) + ;; + recent|default) + COMPREPLY+=($(workouts -w ls --name-only | grep "$2")) + ;; + workout) + COMPREPLY+=($(workouts -w ls --name-only | grep "$2")) + ;; + esac + ;; + workouts) + COMPREPLY+=($(compgen -W "${commands}" -- "$2")) + COMPREPLY+=($(compgen -W "${opts}" -- "$2")) + ;; + *) + if [[ "$utility" == "workouts" ]]; then + COMPREPLY+=($(compgen -W "${commands}" -- "$2")) + COMPREPLY+=($(compgen -W "${opts}" -- "$2")) + fi + ;; + esac +} + +complete -F _workouts workouts diff --git a/configure.ac b/configure.ac index c57fecc..1383659 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_INIT([workouts], [1.0.0]) # Store build files not in main directory AC_CONFIG_AUX_DIR([build-aux]) -AM_INIT_AUTOMAKE([foreign subdir-objects nostdinc -Wall -Werror]) +AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADERS([include/config.h]) @@ -35,7 +35,7 @@ AM_CONDITIONAL([HAVE_VALGRIND], [test -n "$VALGRIND"]) AC_MSG_CHECKING([if debugging]) if test x$enable_debug != xno; then AC_MSG_RESULT(yes) - CFLAGS="-ggdb -O0" + CFLAGS="-ggdb3 -O0" else AC_MSG_RESULT(no) fi diff --git a/include/data.h b/include/data.h index 66264cf..bc0207a 100644 --- a/include/data.h +++ b/include/data.h @@ -7,6 +7,7 @@ #include +#define DB_FILENAME "workouts.db" #define EMPTY_STRING "" // attributes @@ -21,20 +22,20 @@ int attribute_count(); #define ATTRIBUTE_DELETE_SQL "DELETE FROM `" ATTRIBUTE_TABLE_NAME_SQL "` WHERE `name` = ?;" -int attribute_delete(char*); +int attribute_delete(const char*); #define ATTRIBUTE_GET_SQL "SELECT `name` FROM " ATTRIBUTE_TABLE_NAME_SQL " WHERE 1 ORDER BY `order` ASC;" int attribute_get(void (*)(const unsigned char*)); -int attribute_index(char*); +int attribute_index(const char*); void attribute_index_helper(const unsigned char*); #define ATTRIBUTE_INSERT_SQL "INSERT INTO " ATTRIBUTE_TABLE_NAME_SQL " (`name`,`order`) VALUES (?,(SELECT MAX(`order`)+1 FROM `" ATTRIBUTE_TABLE_NAME_SQL "`));" -int attribute_insert(char*); -int attribute_parse(char*,int*,int*); -unsigned int attribute_template(char*,int); +int attribute_insert(const char*); +int attribute_parse(const char*,int*,int*); +unsigned int attribute_template(const char*,int); #define ATTRIBUTE_UPDATE_SQL "UPDATE `" ATTRIBUTE_TABLE_NAME_SQL "` SET `name` = ? WHERE `name` = ?;" -int attribute_update(char*,char*); +int attribute_update(const char*,const char*); // recent #define RECENT_TABLE_NAME_SQL "recent" @@ -48,18 +49,18 @@ int attribute_update(char*,char*); ");" #define RECENT_DELETE_SQL "DELETE FROM `" RECENT_TABLE_NAME_SQL "` WHERE `name` = ? AND `date` = ?;" -int recent_delete(char*,char*); +int recent_delete(const char*,const char*); #define RECENT_GET_BASE_SQL "SELECT name, date FROM `" RECENT_TABLE_NAME_SQL "`" #define RECENT_GET_SQL RECENT_GET_BASE_SQL " ORDER BY date(`date`) DESC,`name` ASC LIMIT ?;" #define RECENT_GET_SEARCH_SQL RECENT_GET_BASE_SQL " WHERE name LIKE ('%' || ? || '%') ORDER BY date(`date`) DESC,`name` ASC LIMIT ?;" -int recent_get(char*,int,void(*)(const unsigned char*,const unsigned char*)); +int recent_get(const char*,int,void(*)(const unsigned char*,const unsigned char*)); #define RECENT_INSERT_SQL "INSERT INTO " RECENT_TABLE_NAME_SQL " (name,date) VALUES (?,?);" -int recent_insert(char*,char*); +int recent_insert(const char*,const char*); #define RECENT_UPDATE_SQL "UPDATE `" RECENT_TABLE_NAME_SQL "` SET date = ? WHERE date = ? AND name = ?;" -int recent_update(char*,char*,char*); +int recent_update(const char*,const char*,const char*); // workouts #define WORKOUT_TABLE_NAME_SQL "workouts" @@ -70,7 +71,7 @@ int recent_update(char*,char*,char*); ");" #define WORKOUT_DELETE_SQL "DELETE FROM `" WORKOUT_TABLE_NAME_SQL "` WHERE `name` = ?;" -int workout_delete(char*); +int workout_delete(const char*); #define WORKOUT_GET_BASE_SQL "SELECT " \ "`" WORKOUT_TABLE_NAME_SQL "`.name, " \ @@ -82,17 +83,17 @@ int workout_delete(char*); #define WORKOUT_GET_SQL WORKOUT_GET_BASE_SQL " WHERE (attributes & ?1) = ?1 AND (~attributes & ?2) = ?2 ORDER BY `last` DESC, `" WORKOUT_TABLE_NAME_SQL "`.name ASC LIMIT ?;" #define WORKOUT_GET_SEARCH_SQL WORKOUT_GET_BASE_SQL " WHERE `" WORKOUT_TABLE_NAME_SQL "`.name LIKE ('%' || ? || '%') AND (attributes & ?2) = ?2 AND (~attributes & ?3) = ?3 ORDER BY `last` DESC, `" WORKOUT_TABLE_NAME_SQL "`.name ASC LIMIT ?;" -int workout_get(char*,char*,int,void (*)(const unsigned char*,int,const unsigned char*)); +int workout_get(const char*,const char*,int,void (*)(const unsigned char*,int,const unsigned char*)); #define WORKOUT_INSERT_SQL "INSERT INTO " WORKOUT_TABLE_NAME_SQL " (name,attributes) VALUES (?,?);" -int workout_insert(char*, unsigned int); +int workout_insert(const char*, unsigned int); -int workout_toggle(char*,char*); +int workout_toggle(const char*,const char*); void workout_toggle_helper(const unsigned char*,int,const unsigned char*); #define WORKOUT_UPDATE_NAME_SQL "UPDATE `" WORKOUT_TABLE_NAME_SQL "` SET name = ? WHERE name = ?;" #define WORKOUT_UPDATE_ATTRIBUTES_SQL "UPDATE `" WORKOUT_TABLE_NAME_SQL "` SET attributes = ? WHERE name = ?;" -int workout_update(char*,char*,int); +int workout_update(const char*,const char*,int); #define WORKOUT_SHIFT_ATTRIBUTE_FLAGS_SQL "UPDATE `" WORKOUT_TABLE_NAME_SQL "` SET attributes = (((attributes<<(?3 - ?1))&?2)>>(?3 - ?1)) + ((attributes>>(?1+1))< #include #include #include #include +#include #include @@ -18,8 +18,6 @@ enum workout_data_type { // global options struct options { - char *db_location; - char *homedir; int rows; enum log_level verbose; enum workout_data_type target; @@ -28,9 +26,9 @@ struct options { extern struct options global_opts; // specific option setters -int opt_set_homedir(char*); +int opt_set_homedir(const char*); void opt_set_log_level(enum log_level); void opt_set_rows(int); void opt_set_target(enum workout_data_type); -#endif \ No newline at end of file +#endif diff --git a/src/add.c b/src/add.c index 6d58354..f6d2805 100644 --- a/src/add.c +++ b/src/add.c @@ -9,6 +9,8 @@ int add(int argc, char **argv) { return add_recent(argc,argv); case WORKOUT_DATA_TYPE_WORKOUT: return add_workout(argc,argv); + default: + return EXIT_FAILURE; } } diff --git a/src/data/attr.c b/src/data/attr.c index 2d06d09..d4b7ba9 100644 --- a/src/data/attr.c +++ b/src/data/attr.c @@ -6,7 +6,7 @@ int attribute_count() { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_prepare_v2(db_p,ATTRIBUTE_COUNT_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } @@ -26,7 +26,7 @@ int attribute_count() { return -1; } -int attribute_delete(char *name) { +int attribute_delete(const char *name) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; int i, index, count, mask; @@ -45,7 +45,7 @@ int attribute_delete(char *name) { i--; } - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_exec(db_p,"BEGIN;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; } @@ -85,7 +85,7 @@ int attribute_get(void (*print)(const unsigned char*)) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_prepare_v2(db_p,ATTRIBUTE_GET_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } @@ -112,7 +112,7 @@ int attribute_get(void (*print)(const unsigned char*)) { struct attribute_indexer { int i; int index; - char *name; + const char *name; }; struct attribute_indexer indexer = { @@ -121,7 +121,7 @@ struct attribute_indexer indexer = { NULL, }; -int attribute_index(char *name) { +int attribute_index(const char *name) { indexer.i = 0; indexer.index = -1; indexer.name = name; @@ -138,11 +138,11 @@ void attribute_index_helper(const unsigned char *name) { indexer.i++; } -int attribute_insert(char *name) { +int attribute_insert(const char *name) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_prepare_v2(db_p,ATTRIBUTE_INSERT_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_bind_text(stmt_p,1,name,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; } @@ -160,7 +160,7 @@ int attribute_insert(char *name) { return -1; } -int attribute_parse(char *str, int *required, int *exclude) { +int attribute_parse(const char *str, int *required, int *exclude) { if(NULL==str) { return -1; } if(NULL==required) { return -1; } @@ -185,7 +185,7 @@ int attribute_parse(char *str, int *required, int *exclude) { return 1; } -unsigned int attribute_template(char *p, int len) { +unsigned int attribute_template(const char *p, int len) { unsigned int attr_flags; attr_flags = 0; @@ -199,11 +199,11 @@ unsigned int attribute_template(char *p, int len) { return attr_flags; } -int attribute_update(char *from, char *to) { +int attribute_update(const char *from, const char *to) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_prepare_v2(db_p,ATTRIBUTE_UPDATE_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_bind_text(stmt_p,1,to,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; } diff --git a/src/data/recent.c b/src/data/recent.c index 2fa96e1..f665eb9 100644 --- a/src/data/recent.c +++ b/src/data/recent.c @@ -1,10 +1,10 @@ #include -int recent_delete(char *workout, char *date) { +int recent_delete(const char *workout, const char *date) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_prepare_v2(db_p,RECENT_DELETE_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_bind_text(stmt_p,1,workout,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; } @@ -23,11 +23,11 @@ int recent_delete(char *workout, char *date) { return -1; } -int recent_get(char *term, int limit, void (*f)(const unsigned char*,const unsigned char*)) { +int recent_get(const char *term, int limit, void (*f)(const unsigned char*,const unsigned char*)) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(NULL==term) { if(sqlite3_prepare_v2(db_p,RECENT_GET_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } @@ -58,11 +58,11 @@ int recent_get(char *term, int limit, void (*f)(const unsigned char*,const unsig return -1; } -int recent_insert(char *workout, char *date) { +int recent_insert(const char *workout, const char *date) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_exec(db_p,"PRAGMA foreign_keys = ON;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; } @@ -83,11 +83,11 @@ int recent_insert(char *workout, char *date) { return -1; } -int recent_update(char *workout, char *from_date, char *to_date) { +int recent_update(const char *workout, const char *from_date, const char *to_date) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_prepare_v2(db_p,RECENT_UPDATE_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_bind_text(stmt_p,1,to_date,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; } diff --git a/src/data/setup.c b/src/data/setup.c index 5d96a32..3b001d5 100644 --- a/src/data/setup.c +++ b/src/data/setup.c @@ -3,7 +3,7 @@ int setup() { sqlite3 *db_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_exec(db_p,CREATE_SCHEMA_SQL,NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; } @@ -13,4 +13,4 @@ int setup() { cleanup: if(db_p!=NULL) { sqlite3_close_v2(db_p); } return -1; -} \ No newline at end of file +} diff --git a/src/data/workout.c b/src/data/workout.c index 735728d..d6328a2 100644 --- a/src/data/workout.c +++ b/src/data/workout.c @@ -1,10 +1,10 @@ #include -int workout_delete(char *name) { +int workout_delete(const char *name) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_exec(db_p,"PRAGMA foreign_keys = ON;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; } @@ -24,7 +24,7 @@ int workout_delete(char *name) { return -1; } -int workout_get(char *term, char *filter, int limit, void (*print_row)(const unsigned char*,int,const unsigned char*)) { +int workout_get(const char *term, const char *filter, int limit, void (*print_row)(const unsigned char*,int,const unsigned char*)) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; @@ -34,7 +34,7 @@ int workout_get(char *term, char *filter, int limit, void (*print_row)(const uns if(attribute_parse(filter,&required,&exclude)<0) { return -1; } } - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(NULL==term) { if(sqlite3_prepare_v2(db_p,WORKOUT_GET_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } @@ -70,11 +70,11 @@ int workout_get(char *term, char *filter, int limit, void (*print_row)(const uns return -1; } -int workout_insert(char *name, unsigned int flags) { +int workout_insert(const char *name, unsigned int flags) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_prepare_v2(db_p,WORKOUT_INSERT_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_bind_text(stmt_p,1,name,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; } @@ -94,7 +94,7 @@ int workout_insert(char *name, unsigned int flags) { } struct workout_toggler { - char *name; + const char *name; int attributes; }; @@ -103,7 +103,7 @@ struct workout_toggler toggler = { 0 }; -int workout_toggle(char *name, char *attribute) { +int workout_toggle(const char *name, const char *attribute) { int index = attribute_index(attribute); if(index<0) { return -1; } @@ -134,11 +134,11 @@ void workout_toggle_helper(const unsigned char *name, int flags, const unsigned } } -int workout_update(char *old, char *name, int flags) { +int workout_update(const char *old, const char *name, int flags) { sqlite3 *db_p = NULL; sqlite3_stmt *stmt_p = NULL; - if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } + if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; } if(sqlite3_exec(db_p,"PRAGMA foreign_keys = ON;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; } diff --git a/src/date.c b/src/date.c index 43ec5da..f255bcb 100644 --- a/src/date.c +++ b/src/date.c @@ -1,6 +1,6 @@ #include -int date_parse(char *str, char *buf, size_t len) { +int date_parse(const char *str, char *buf, size_t len) { assert(DATE_BUF_LEN==len); if(NULL==str) { diff --git a/src/default.c b/src/default.c index 0469748..33a83ac 100644 --- a/src/default.c +++ b/src/default.c @@ -1,30 +1,12 @@ #include struct options global_opts = { - NULL, /* db_location */ - NULL, /* homedir */ -1, /* rows */ - 0 /* verbose */ + 0, /* verbose */ + 0 /* target */ }; int defaults() { - char *p; - - // homedir - p = getenv("WORKOUTS_HOME"); - if(p==NULL) { - p = getenv("HOME"); - if(NULL==p) { - log_err("HOME or WORKOUTS_HOME env variable must be defined\n"); - return -1; - } - } - - if(opt_set_homedir(p)<0) { - log_err("HOME or WORKOUTS_HOME env value invalid\n"); - return -1; - } - opt_set_rows(-1); opt_set_log_level(LOG_LEVEL_DEFAULT); @@ -32,4 +14,4 @@ int defaults() { opt_set_target(WORKOUT_DATA_TYPE_DEFAULT); return 0; -} \ No newline at end of file +} diff --git a/src/ls/attr.c b/src/ls/attr.c index e0be01f..efa10eb 100644 --- a/src/ls/attr.c +++ b/src/ls/attr.c @@ -1,5 +1,7 @@ #include +static void print_attribute(const unsigned char*); + int ls_attribute() { if(attribute_get(&print_attribute)<0) { return EXIT_FAILURE; } @@ -8,6 +10,6 @@ int ls_attribute() { #define ATTRIBUTE_PRINT_FORMAT "%s\n" -void print_attribute(const unsigned char *name) { +static void print_attribute(const unsigned char *name) { printf(ATTRIBUTE_PRINT_FORMAT,name); } diff --git a/src/ls/last.c b/src/ls/last.c index b52e128..bf36078 100644 --- a/src/ls/last.c +++ b/src/ls/last.c @@ -1,14 +1,20 @@ #include -int last_done(char *search_term, char *filter_p) { +static void generate_last_done(const unsigned char*,int, const unsigned char*); +static void print_last_done(); + +int last_done(const char *search_term, const char *filter_p) { int i; + printf(WORKOUT_PRINT_ATTRIBUTE_HEADER,WORKOUT_PRINT_ATTRIBUTE_LABEL); + if(attribute_get(&print_header)<0) { return EXIT_FAILURE; } + printf(WORKOUT_PRINT_ATTRIBUTE_END); + helper.days_ago = malloc(helper.attr_count*sizeof(int)); if(NULL==helper.days_ago) { return EXIT_FAILURE; } for(i=0;i0) { + if((flags&1)>0) { memset(&when,0,sizeof(struct tm)); - if(sscanf(date,"%d-%d-%d",&YY,&MM,&DD)!=3) { + if(sscanf((const char*)date,"%d-%d-%d",&YY,&MM,&DD)!=3) { log_err(LS_GENERATE_LAST_DONE_INVALID_DATE_OUTPUT); } @@ -56,7 +63,7 @@ void generate_last_done(const unsigned char *workout, int flags, const unsigned #define LAST_DONE_PRINT_ATTRIBUTE "%d\t" #define LAST_DONE_PRINT_SUGGESTED_LABEL "Suggested filter: " -void print_last_done() { +static void print_last_done() { int i, max = 0; printf(LAST_DONE_PRINT_HEADER,LAST_DONE_PRINT_LABEL); for(i=0;i -struct ls_helper helper = { - 0, - 0 -}; +struct ls_helper helper; + +static void helper_init(); int ls(int argc, char **argv) { + helper_init(); + switch(global_opts.target) { case WORKOUT_DATA_TYPE_ATTRIBUTE: return ls_attribute(); @@ -14,5 +15,14 @@ int ls(int argc, char **argv) { case WORKOUT_DATA_TYPE_DEFAULT: case WORKOUT_DATA_TYPE_WORKOUT: return ls_workout(argc,argv); + default: + return EXIT_FAILURE; } } + +static void helper_init() { + helper.i = 0; + helper.attr_count = 0; + helper.days_ago = NULL; + time(&helper.now); +} diff --git a/src/ls/name.c b/src/ls/name.c new file mode 100644 index 0000000..bb2998c --- /dev/null +++ b/src/ls/name.c @@ -0,0 +1,20 @@ +#include + +static void print_workout_name(const unsigned char*,int,const unsigned char*); + +int ls_name_only(const char *search_term, const char *filter_p) { + if(workout_get( + search_term, /* term */ + filter_p, /* filter */ + global_opts.rows, /* limit */ + &print_workout_name /* print function */ + )<0) { return EXIT_FAILURE; } + + return EXIT_SUCCESS; +} + +#define WORKOUT_PRINT_NAME_ONLY "%s\n" + +static void print_workout_name(const unsigned char *name, int attr_flags, const unsigned char *last) { + printf(WORKOUT_PRINT_NAME_ONLY,name); +} diff --git a/src/ls/recent.c b/src/ls/recent.c index 8dbbeff..a97a382 100644 --- a/src/ls/recent.c +++ b/src/ls/recent.c @@ -1,5 +1,7 @@ #include +static void print_recent(const unsigned char*,const unsigned char*); + int ls_recent(int argc, char **argv) { if(recent_get( (argc>=2)?argv[1]:NULL, /* term */ @@ -15,6 +17,6 @@ int ls_recent(int argc, char **argv) { #define RECENT_PRINT_FORMAT "%-30.30s\t%s\n" -void print_recent(const unsigned char *workout, const unsigned char *date) { +static void print_recent(const unsigned char *workout, const unsigned char *date) { printf(RECENT_PRINT_FORMAT,workout,date); } diff --git a/src/ls/single.c b/src/ls/single.c index 2d2db86..dd79c54 100644 --- a/src/ls/single.c +++ b/src/ls/single.c @@ -1,6 +1,8 @@ #include -int ls_single(char *search_term, char *filter_p) { +static void print_single(const unsigned char*,int,const unsigned char*); + +int ls_single(const char *search_term, const char *filter_p) { if(workout_get( search_term, /* term */ filter_p, /* filter */ @@ -19,19 +21,19 @@ int ls_single(char *search_term, char *filter_p) { #define WORKOUT_PRINT_SINGLE_LAST_DONE "[Last done: %s]\n" #define WORKOUT_PRINT_SINGLE_LAST_NULL "[Last done: N/A]\n" -void print_single(const unsigned char *name, int attr_flags, const unsigned char *last) { +static void print_single(const unsigned char *name, int attr_flags, const unsigned char *last) { int i; - printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE_HEADER,strlen(name),WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL); + printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE_HEADER,(int)strlen((const char*)name),WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL); if(attribute_get(&print_header)<0) { log_err(LS_PRINT_SINGLE_ATTRIBUTE_GET_FAILED); return; } printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE_END); - printf(WORKOUT_PRINT_SINGLE_NAME,strlen(WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL),name); + printf(WORKOUT_PRINT_SINGLE_NAME,(int)strlen(WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL),name); for(i=0;i0)?"yes":"no"); + printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE,((attr_flags&1)>0)?"yes":"no"); attr_flags >>= 1; } diff --git a/src/ls/workout.c b/src/ls/workout.c index 30d6667..516ce45 100644 --- a/src/ls/workout.c +++ b/src/ls/workout.c @@ -1,12 +1,12 @@ #include -#define WORKOUT_PRINT_ATTRIBUTE_HEADER "%-30.30s\t" -#define WORKOUT_PRINT_ATTRIBUTE_LABEL "Attributes:" -#define WORKOUT_PRINT_ATTRIBUTE_END "\n" +static int ls_with_attributes(const char*,const char*); +static void print_workout(const unsigned char*,int,const unsigned char*); static struct option ls_long_options[] = { {"filter", required_argument, 0, 'f'}, {"last-done", no_argument, 0, 1}, + {"name-only", no_argument, 0, 2}, {0,0,0,0} }; @@ -23,9 +23,10 @@ int ls_workout(int argc, char **argv) { * x: can have attribute */ - int c, j, last_done_flag = 0; + int c; char *filter_p = NULL; char *search_term = NULL; + enum ls_format format = LS_FORMAT_DEFAULT; // reset optind; see `man getopt.3` NOTES section for justification optind = 0; @@ -46,11 +47,22 @@ int ls_workout(int argc, char **argv) { return EXIT_FAILURE; break; - case 1: - last_done_flag = 1; + case 1: // --last-done + format = LS_FORMAT_LAST_DONE; + break; + case 2: // --name-only + format = LS_FORMAT_NAME_ONLY; break; case 'f': filter_p = optarg; + { + int count; + if((count = attribute_count())<0) { return EXIT_FAILURE; } + if(strlen(filter_p)!=count) { + log_err(LS_MESSAGE_INVALID_FILTER,strlen(filter_p),count); + return EXIT_FAILURE; + } + } break; case '?': default: @@ -63,16 +75,29 @@ int ls_workout(int argc, char **argv) { search_term = argv[optind]; } - if((global_opts.rows==1)&&(!last_done_flag)) { - return ls_single(search_term,filter_p); + switch(format) { + case LS_FORMAT_LAST_DONE: + return last_done(search_term, filter_p); + case LS_FORMAT_NAME_ONLY: + return ls_name_only(search_term,filter_p); + case LS_FORMAT_WITH_ATTRIBUTES: + if(global_opts.rows==1) { + return ls_single(search_term,filter_p); + } else { + return ls_with_attributes(search_term,filter_p); + } + break; + default: + log_err(LS_MESSAGE_INVALID_FORMAT); + return EXIT_FAILURE; } +} +static int ls_with_attributes(const char *search_term, const char *filter_p) { printf(WORKOUT_PRINT_ATTRIBUTE_HEADER,WORKOUT_PRINT_ATTRIBUTE_LABEL); - if(attribute_get(&print_header)<0) { return -1; } + if(attribute_get(&print_header)<0) { return EXIT_FAILURE; } printf(WORKOUT_PRINT_ATTRIBUTE_END); - if(last_done_flag) { return last_done(search_term, filter_p); } - if(workout_get( search_term, /* term */ filter_p, /* filter */ @@ -88,12 +113,12 @@ int ls_workout(int argc, char **argv) { #define WORKOUT_PRINT_LAST_DONE "[Last done: %s]\n" #define WORKOUT_PRINT_LAST_NULL "[Last done: N/A]\n" -void print_workout(const unsigned char *name, int attr_flags, const unsigned char *last) { +static void print_workout(const unsigned char *name, int attr_flags, const unsigned char *last) { int i; printf(WORKOUT_PRINT_NAME,name); for(i=0;i0)?"yes":"no"); + printf(WORKOUT_PRINT_ATTRIBUTE,((attr_flags&1)>0)?"yes":"no"); attr_flags >>= 1; } printf((NULL==last)?WORKOUT_PRINT_LAST_NULL:WORKOUT_PRINT_LAST_DONE,last); diff --git a/src/opt/homedir.c b/src/opt/homedir.c index da7dd75..b65d0b3 100644 --- a/src/opt/homedir.c +++ b/src/opt/homedir.c @@ -1,44 +1,10 @@ #include -#define DB_FILENAME "workouts.db" -#define DB_FILENAME_LENGTH 11 - -int opt_set_homedir(char *to_set) { - char *homedir, *db_location; - int len = strlen(to_set); - if(len<1) { return -1; } - - homedir = malloc(sizeof(char)*(len+1)); - if(NULL==homedir) { return -1; } - - db_location = malloc(sizeof(char)*(len+DB_FILENAME_LENGTH+2)); - if(NULL==db_location) { return -1; } - - strcpy(homedir,to_set); - strcpy(db_location,homedir); - - for(int i=0;;i++) { - if(db_location[i]=='\0') { - if(db_location[i-1]!='/') { - strcat(db_location,"/"); - } - break; - } - } - - strcat(db_location,DB_FILENAME); - - DIR *dir = opendir(homedir); - if(NULL==dir) { +int opt_set_homedir(const char *to_set) { + if(chdir(to_set)!=0) { + perror("chdir"); return -1; } - closedir(dir); - - if(global_opts.homedir!=NULL) { free(global_opts.homedir); } - if(global_opts.db_location!=NULL) { free(global_opts.db_location); } - global_opts.homedir = homedir; - global_opts.db_location = db_location; - return 1; } diff --git a/src/opt/rows.c b/src/opt/rows.c index 18b67b5..0e50c72 100644 --- a/src/opt/rows.c +++ b/src/opt/rows.c @@ -2,4 +2,4 @@ void opt_set_rows(int to_set) { global_opts.rows = to_set; -} \ No newline at end of file +} diff --git a/src/rm.c b/src/rm.c index 0f9573e..3afe076 100644 --- a/src/rm.c +++ b/src/rm.c @@ -9,6 +9,8 @@ int rm(int argc, char **argv) { return rm_recent(argc,argv); case WORKOUT_DATA_TYPE_WORKOUT: return rm_workout(argc,argv); + default: + return EXIT_FAILURE; } } @@ -58,4 +60,4 @@ int rm_workout(int argc, char **argv) { log_msg(RM_MESSAGE_WORKOUT_DELETED,argv[1]); return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/src/update.c b/src/update.c index d8fe0df..922f43d 100644 --- a/src/update.c +++ b/src/update.c @@ -9,6 +9,8 @@ int update(int argc, char **argv) { return update_recent(argc,argv); case WORKOUT_DATA_TYPE_WORKOUT: return update_workout(argc,argv); + default: + return EXIT_FAILURE; } } diff --git a/src/usage.c b/src/usage.c index d757b9a..52d463e 100644 --- a/src/usage.c +++ b/src/usage.c @@ -3,7 +3,7 @@ void usage() { log_err("Usage:\n"); log_err("\tworkouts [options] add [!name] [date] [attribute filter]\n"); - log_err("\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [search term]\n"); + log_err("\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [--name-only] [search term]\n"); log_err("\tworkouts [options] rm [!name] [date]\n"); log_err("\tworkouts [options] toggle [!workout name] [!attr]\n"); log_err("\tworkouts [options] update [workout name] [!from] [!to]\n"); diff --git a/test/integration/package-lock.json b/test/integration/package-lock.json index 3057817..2ad0b88 100644 --- a/test/integration/package-lock.json +++ b/test/integration/package-lock.json @@ -1,8 +1,987 @@ { "name": "workouts-integration-tests", "version": "0.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "workouts-integration-tests", + "version": "0.0.0", + "license": "UNLICENSED", + "devDependencies": { + "mocha": "^8.4.0" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "3.0.0", + "picomatch": "2.3.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "1.0.2", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "dependencies": { + "anymatch": "3.1.2", + "braces": "3.0.2", + "fsevents": "2.3.2", + "glob-parent": "5.1.2", + "is-binary-path": "2.1.0", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "readdirp": "3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "4.2.2", + "strip-ansi": "6.0.0", + "wrap-ansi": "7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "8.0.0", + "is-fullwidth-code-point": "3.0.0", + "strip-ansi": "6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "6.0.0", + "path-exists": "4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.4", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "dependencies": { + "argparse": "2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "4.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "1.1.11" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1.0.2" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "3.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "2.3.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "2.1.0" + } + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "2.1.1" + } + }, + "node_modules/workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "4.3.0", + "string-width": "4.2.2", + "strip-ansi": "6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "8.0.0", + "is-fullwidth-code-point": "3.0.0", + "strip-ansi": "6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "7.0.4", + "escalade": "3.1.1", + "get-caller-file": "2.0.5", + "require-directory": "2.1.1", + "string-width": "4.2.2", + "y18n": "5.0.8", + "yargs-parser": "20.2.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "6.2.0", + "decamelize": "4.0.0", + "flat": "5.0.2", + "is-plain-obj": "2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "8.0.0", + "is-fullwidth-code-point": "3.0.0", + "strip-ansi": "6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + } + } + }, "dependencies": { "@ungap/promise-all-settled": { "version": "1.1.2", diff --git a/test/integration/test/basic.test.js b/test/integration/test/basic.test.js index a11baac..9ba8c0c 100644 --- a/test/integration/test/basic.test.js +++ b/test/integration/test/basic.test.js @@ -1,5 +1,6 @@ const assert = require('assert'); const {cwd} = require('process'); +const path = require('path'); const util = require('util'); const exec = util.promisify(require('child_process').exec); @@ -19,7 +20,7 @@ describe('basic tests', () => { it('should print usage options when run with --help option', async() => { let usage = `Usage:\n`; usage += `\tworkouts [options] add [!name] [date] [attribute filter]\n`; - usage += `\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [search term]\n`; + usage += `\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [--name-only] [search term]\n`; usage += `\tworkouts [options] rm [!name] [date]\n`; usage += `\tworkouts [options] toggle [!workout name] [!attr]\n`; usage += `\tworkouts [options] update [workout name] [!from] [!to]\n`; @@ -52,8 +53,17 @@ describe('basic tests', () => { }); it(`should throw when given option --homedir which doesn't exist`, async() => { - await assert.rejects(async() => await exec(`${workouts} --homedir=${pwd}/doesnt_exist`)); - await assert.rejects(async() => await exec(`${workouts} -d ${pwd}/doesnt_exist`)); + const baddir = path.join(cwd(),'doesnt_exist'); + await assert.rejects(async() => await exec(`${workouts} --homedir=${baddir}`),(err) => { + assert.strictEqual(err.code,1); + assert.strictEqual(err.stderr,`chdir: No such file or directory\n`); + return true; + }); + await assert.rejects(async() => await exec(`${workouts} -d ${baddir}`),(err) => { + assert.strictEqual(err.code,1); + assert.strictEqual(err.stderr,`chdir: No such file or directory\n`); + return true; + }); }); after(async() => { diff --git a/test/integration/test/ls.integration.test.js b/test/integration/test/ls.integration.test.js index 6b2a548..788edb4 100644 --- a/test/integration/test/ls.integration.test.js +++ b/test/integration/test/ls.integration.test.js @@ -11,11 +11,24 @@ describe('ls integration tests', () => { const workouts = `../../workouts -d ${cwd()}`; + // helper methods for setting dates + const now = new Date(); + const daysAgo = (offset) => { + const date = new Date(); + date.setDate(now.getDate() - offset); + const y = date.getFullYear(); + const m = (date.getMonth()+1).toString().padStart(2,'0'); + const d = (date.getDate()).toString().padStart(2,'0'); + return `${y}-${m}-${d}`; + }; + const recent = [ - {name:"workout1",date:"2020-07-10"}, - {name:"apple",date:"2020-07-09"}, - {name:"test",date:"2020-07-09"}, - {name:"workout2",date:"2020-07-09"} + {name:"workout1",date:daysAgo(0)}, + {name:"apple",date:daysAgo(1)}, + {name:"test",date:daysAgo(1)}, + {name:"workout2",date:daysAgo(2)}, + {name:"workout4",date:daysAgo(10)}, + {name:"workout3",date:daysAgo(20)} ]; before(async() => { @@ -32,11 +45,17 @@ describe('ls integration tests', () => { beforeEach(async() => { await assert.doesNotReject(async() => await exec(`${workouts} --attr add lower`)); await assert.doesNotReject(async() => await exec(`${workouts} --attr add upper`)); - - await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout1 01`)); - await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout2 10`)); - await assert.doesNotReject(async() => await exec(`${workouts} --workout add test 00`)); - await assert.doesNotReject(async() => await exec(`${workouts} --workout add apple 11`)); + await assert.doesNotReject(async() => await exec(`${workouts} --attr add cardio`)); + await assert.doesNotReject(async() => await exec(`${workouts} --attr add back`)); + await assert.doesNotReject(async() => await exec(`${workouts} --attr add core`)); + await assert.doesNotReject(async() => await exec(`${workouts} --attr add other`)); + + await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout1 010000`)); + await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout2 100000`)); + await assert.doesNotReject(async() => await exec(`${workouts} --workout add test 001000`)); + await assert.doesNotReject(async() => await exec(`${workouts} --workout add apple 110000`)); + await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout3 110011`)); + await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout4 001100`)); for(const i of recent) { await assert.doesNotReject(async() => await exec(`${workouts} --recent add ${i.name} ${i.date}`)); @@ -49,7 +68,7 @@ describe('ls integration tests', () => { describe('ls_attribute', () => { it('should successfully list all attributes in order they were added', async() => { - const attributes = ["lower","upper"]; + const attributes = ["lower","upper", "cardio", "back", "core", "other"]; await assert.doesNotReject(async() => { const {stdout,stderr} = await exec(`${workouts} --attr ls`); @@ -81,7 +100,7 @@ describe('ls integration tests', () => { it('should success filter recent workouts', async() => { const {stdout,stderr} = await exec(`${workouts} --recent ls test`); - const expected = `test`.padEnd(30,' ') + `\t2020-07-09\n`; + const expected = `test`.padEnd(30,' ') + `\t${daysAgo(1)}\n`; assert.strictEqual(stdout,expected); }); }); @@ -93,15 +112,19 @@ describe('ls integration tests', () => { const {stdout,stderr} = await exec(`${workouts}`); let expected = `Attributes:`.padEnd(30,' '); - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `workout1`.padEnd(30,' '); - expected += `\tno\tyes\t[Last done: 2020-07-10]\n`; + expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`; expected += `apple`.padEnd(30,' '); - expected += `\tyes\tyes\t[Last done: 2020-07-09]\n`; + expected += `\tyes\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`; expected += `test`.padEnd(30,' '); - expected += `\tno\tno\t[Last done: 2020-07-09]\n`; + expected += `\tno\tno\tyes\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`; expected += `workout2`.padEnd(30,' '); - expected += `\tyes\tno\t[Last done: 2020-07-09]\n`; + expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`; + expected += `workout4`.padEnd(30,' '); + expected += `\tno\tno\tyes\tyes\tno\tno\t[Last done: ${daysAgo(10)}]\n`; + expected += `workout3`.padEnd(30,' '); + expected += `\tyes\tyes\tno\tno\tyes\tyes\t[Last done: ${daysAgo(20)}]\n`; assert.strictEqual(stdout,expected); }); @@ -112,9 +135,9 @@ describe('ls integration tests', () => { const {stdout,stderr} = await exec(`${workouts} --rows=1 ls`); let expected = `Attributes:`; - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `workout1 `; - expected += `\tno\tyes\t[Last done: 2020-07-10]\n`; + expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`; assert.strictEqual(stdout,expected); const res = await exec(`${workouts} --workout update workout2 workoutworkoutworkoutworkoutworkout`); @@ -123,9 +146,9 @@ describe('ls integration tests', () => { const res2 = await exec(`${workouts} --rows=1 ls workoutworkout`); expected = `Attributes: `; - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `workoutworkoutworkoutworkoutworkout`; - expected += `\tyes\tno\t[Last done: 2020-07-09]\n`; + expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`; assert.strictEqual(res2.stdout,expected); }); }); @@ -135,11 +158,15 @@ describe('ls integration tests', () => { const {stdout,stderr} = await exec(`${workouts} ls workout`); let expected = `Attributes:`.padEnd(30,' '); - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `workout1`.padEnd(30,' '); - expected += `\tno\tyes\t[Last done: 2020-07-10]\n`; + expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`; expected += `workout2`.padEnd(30,' '); - expected += `\tyes\tno\t[Last done: 2020-07-09]\n`; + expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`; + expected += `workout4`.padEnd(30,' '); + expected += `\tno\tno\tyes\tyes\tno\tno\t[Last done: ${daysAgo(10)}]\n`; + expected += `workout3`.padEnd(30,' '); + expected += `\tyes\tyes\tno\tno\tyes\tyes\t[Last done: ${daysAgo(20)}]\n`; assert.strictEqual(stdout,expected); }); @@ -147,53 +174,133 @@ describe('ls integration tests', () => { it('should reject when `ls` term is not given and attempting to filter', async() => { await assert.rejects(async() => { - await exec(`${workouts} --filter 01`); + await exec(`${workouts} --filter 010000`); }); }); + + it('should throw when given a filter with the wrong amount of attributes', async() => { + let num = 1; + const err = (err) => { + assert.strictEqual(err.code,1); + if(num===6) { num++; } + assert.strictEqual(err.stderr,`invalid filter; wrong number of attributes given (${num++} != 6)\n`); + return true; + }; + + await assert.rejects(async() => { + await exec(`${workouts} ls --filter 0`); + },err); + await assert.rejects(async() => { + await exec(`${workouts} ls --filter 01`); + },err); + + await assert.rejects(async() => { + await exec(`${workouts} ls --filter 010`); + },err); + await assert.rejects(async() => { + await exec(`${workouts} ls --filter 0100`); + },err); + await assert.rejects(async() => { + await exec(`${workouts} ls --filter 01000`); + },err); + await assert.rejects(async() => { + await exec(`${workouts} ls --filter 0100000`); + },err); + }); it('should successfully filter workouts by attribute', async() => { await assert.doesNotReject(async() => { - const {stdout,stderr} = await exec(`${workouts} ls --filter 10`); + const {stdout,stderr} = await exec(`${workouts} ls --filter 100000`); let expected = `Attributes:`.padEnd(30,' '); - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `workout2`.padEnd(30,' '); - expected += `\tyes\tno\t[Last done: 2020-07-09]\n`; + expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`; assert.strictEqual(stdout,expected); }); await assert.doesNotReject(async() => { - const {stdout,stderr} = await exec(`${workouts} ls --filter 01`); + const {stdout,stderr} = await exec(`${workouts} ls --filter 010000`); let expected = `Attributes:`.padEnd(30,' '); - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `workout1`.padEnd(30,' '); - expected += `\tno\tyes\t[Last done: 2020-07-10]\n`; + expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`; assert.strictEqual(stdout,expected); }); await assert.doesNotReject(async() => { - const {stdout,stderr} = await exec(`${workouts} ls --filter 1x`); + const {stdout,stderr} = await exec(`${workouts} ls --filter 1xxxxx`); let expected = `Attributes:`.padEnd(30,' '); - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `apple`.padEnd(30,' '); - expected += `\tyes\tyes\t[Last done: 2020-07-09]\n`; + expected += `\tyes\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`; expected += `workout2`.padEnd(30,' '); - expected += `\tyes\tno\t[Last done: 2020-07-09]\n`; + expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`; + expected += `workout3`.padEnd(30,' '); + expected += `\tyes\tyes\tno\tno\tyes\tyes\t[Last done: ${daysAgo(20)}]\n`; assert.strictEqual(stdout,expected); }); await assert.doesNotReject(async() => { - const {stdout,stderr} = await exec(`${workouts} ls --filter 00`); + const {stdout,stderr} = await exec(`${workouts} ls --filter 00xxxx`); let expected = `Attributes:`.padEnd(30,' '); - expected += `\tlower\tupper\t\n`; + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; expected += `test`.padEnd(30,' '); - expected += `\tno\tno\t[Last done: 2020-07-09]\n`; + expected += `\tno\tno\tyes\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`; + expected += `workout4`.padEnd(30,' '); + expected += `\tno\tno\tyes\tyes\tno\tno\t[Last done: ${daysAgo(10)}]\n`; + + assert.strictEqual(stdout,expected); + }); + }); + + it('should successfully generate last done array when given --last-done option', async() => { + await assert.doesNotReject(async() => { + const {stdout,stderr} = await exec(`${workouts} ls --last-done`); + let expected = `Attributes:`.padEnd(30,' '); + expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`; + expected += `Last Done:`.padEnd(30,' '); + expected += `\t1\t0\t1\t10\t20\t20\t\n`; + expected += `Suggested filter: xxxx11\n`; + + assert.strictEqual(stdout,expected); + }); + }); + + + it('should successfully print only workout names when given --name-only option', async() => { + await assert.doesNotReject(async() => { + const {stdout,stderr} = await exec(`${workouts} ls --name-only`); + let expected = `workout1\n`; + expected += `apple\n`; + expected += `test\n`; + expected += `workout2\n`; + expected += `workout4\n`; + expected += `workout3\n`; + + assert.strictEqual(stdout,expected); + }); + + await assert.doesNotReject(async() => { + const {stdout,stderr} = await exec(`${workouts} ls --name-only workout`); + let expected = `workout1\n`; + expected += `workout2\n`; + expected += `workout4\n`; + expected += `workout3\n`; + + assert.strictEqual(stdout,expected); + }); + + await assert.doesNotReject(async() => { + const {stdout,stderr} = await exec(`${workouts} ls --name-only -f 11x0xx`); + let expected = `apple\n`; + expected += `workout3\n`; assert.strictEqual(stdout,expected); }); diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am index 1147729..120d633 100644 --- a/test/unit/Makefile.am +++ b/test/unit/Makefile.am @@ -1,8 +1,6 @@ AM_CPPFLAGS = \ - -I$(top_builddir)/include/ \ - -I$(top_builddir)/test/unit/ \ - -I$(top_srcdir)/include/ \ - -I$(top_srcdir)/test/unit/ + -Wall \ + -Werror EXTRA_DIST = \ test_utils.h \ @@ -10,7 +8,6 @@ EXTRA_DIST = \ data.attr.tests.h \ data.recent.tests.h \ data.workout.tests.h \ - ls.tests.h \ toggle.tests.h \ update.tests.h @@ -24,70 +21,72 @@ endif common_SOURCES = test_utils.c -TEST_SRC_DIR = $(top_srcdir)/src - -common_SOURCES += $(TEST_SRC_DIR)/default.c $(TEST_SRC_DIR)/log.c -common_SOURCES += $(TEST_SRC_DIR)/data/setup.c -common_SOURCES += $(TEST_SRC_DIR)/opt/homedir.c $(TEST_SRC_DIR)/opt/loglvl.c $(TEST_SRC_DIR)/opt/rows.c $(TEST_SRC_DIR)/opt/target.c +common_SOURCES += $(top_srcdir)/src/default.c $(top_srcdir)/src/log.c +common_SOURCES += $(top_srcdir)/src/data/setup.c +common_SOURCES += $(top_srcdir)/src/opt/homedir.c +common_SOURCES += $(top_srcdir)/src/opt/loglvl.c +common_SOURCES += $(top_srcdir)/src/opt/rows.c +common_SOURCES += $(top_srcdir)/src/opt/target.c add_tests_SOURCES = \ $(common_SOURCES) \ add.tests.c \ - $(TEST_SRC_DIR)/add.c \ - $(TEST_SRC_DIR)/data/attr.c \ - $(TEST_SRC_DIR)/data/recent.c \ - $(TEST_SRC_DIR)/data/workout.c \ - $(TEST_SRC_DIR)/date.c \ - $(TEST_SRC_DIR)/usage.c + $(top_srcdir)/src/add.c \ + $(top_srcdir)/src/data/attr.c \ + $(top_srcdir)/src/data/recent.c \ + $(top_srcdir)/src/data/workout.c \ + $(top_srcdir)/src/date.c \ + $(top_srcdir)/src/usage.c data_attr_tests_SOURCES = \ $(common_SOURCES) \ data.attr.tests.c \ - $(TEST_SRC_DIR)/data/attr.c \ - $(TEST_SRC_DIR)/data/workout.c + $(top_srcdir)/src/data/attr.c \ + $(top_srcdir)/src/data/workout.c data_recent_tests_SOURCES = \ $(common_SOURCES) \ data.recent.tests.c \ - $(TEST_SRC_DIR)/data/recent.c \ - $(TEST_SRC_DIR)/data/workout.c \ - $(TEST_SRC_DIR)/data/attr.c + $(top_srcdir)/src/data/recent.c \ + $(top_srcdir)/src/data/workout.c \ + $(top_srcdir)/src/data/attr.c data_workout_tests_SOURCES = \ $(common_SOURCES) \ data.workout.tests.c \ - $(TEST_SRC_DIR)/data/workout.c \ - $(TEST_SRC_DIR)/data/attr.c \ - $(TEST_SRC_DIR)/data/recent.c + $(top_srcdir)/src/data/workout.c \ + $(top_srcdir)/src/data/attr.c \ + $(top_srcdir)/src/data/recent.c ls_tests_SOURCES = \ $(common_SOURCES) \ ls.tests.c \ - $(TEST_SRC_DIR)/data/attr.c \ - $(TEST_SRC_DIR)/data/recent.c \ - $(TEST_SRC_DIR)/data/workout.c \ - $(TEST_SRC_DIR)/ls/attr.c \ - $(TEST_SRC_DIR)/ls/head.c \ - $(TEST_SRC_DIR)/ls/last.c \ - $(TEST_SRC_DIR)/ls/ls.c \ - $(TEST_SRC_DIR)/ls/recent.c \ - $(TEST_SRC_DIR)/ls/single.c \ - $(TEST_SRC_DIR)/ls/workout.c \ - $(TEST_SRC_DIR)/usage.c + $(top_srcdir)/src/data/attr.c \ + $(top_srcdir)/src/data/recent.c \ + $(top_srcdir)/src/data/workout.c \ + $(top_srcdir)/src/ls/attr.c \ + $(top_srcdir)/src/ls/head.c \ + $(top_srcdir)/src/ls/last.c \ + $(top_srcdir)/src/ls/ls.c \ + $(top_srcdir)/src/ls/name.c \ + $(top_srcdir)/src/ls/recent.c \ + $(top_srcdir)/src/ls/single.c \ + $(top_srcdir)/src/ls/workout.c \ + $(top_srcdir)/src/usage.c toggle_tests_SOURCES = \ $(common_SOURCES) \ toggle.tests.c \ - $(TEST_SRC_DIR)/data/attr.c \ - $(TEST_SRC_DIR)/data/workout.c \ - $(TEST_SRC_DIR)/toggle.c \ - $(TEST_SRC_DIR)/usage.c + $(top_srcdir)/src/data/attr.c \ + $(top_srcdir)/src/data/workout.c \ + $(top_srcdir)/src/toggle.c \ + $(top_srcdir)/src/usage.c update_tests_SOURCES = \ $(common_SOURCES) \ update.tests.c \ - $(TEST_SRC_DIR)/data/attr.c \ - $(TEST_SRC_DIR)/data/recent.c \ - $(TEST_SRC_DIR)/data/workout.c \ - $(TEST_SRC_DIR)/date.c \ - $(TEST_SRC_DIR)/update.c + $(top_srcdir)/src/data/attr.c \ + $(top_srcdir)/src/data/recent.c \ + $(top_srcdir)/src/data/workout.c \ + $(top_srcdir)/src/date.c \ + $(top_srcdir)/src/update.c diff --git a/test/unit/add.tests.c b/test/unit/add.tests.c index ed1cb59..8f29870 100644 --- a/test/unit/add.tests.c +++ b/test/unit/add.tests.c @@ -7,7 +7,7 @@ int main() { add_recent_basic_test(); add_workout_basic_test(); - clean(); + clean_env(); return EXIT_SUCCESS; } @@ -90,4 +90,4 @@ void add_workout_basic_test() { assert(EXIT_FAILURE==add(3,argv)); reset_env(); -} \ No newline at end of file +} diff --git a/test/unit/data.attr.tests.c b/test/unit/data.attr.tests.c index 9aabd0f..0e2e7ae 100644 --- a/test/unit/data.attr.tests.c +++ b/test/unit/data.attr.tests.c @@ -11,7 +11,7 @@ int main() { attribute_insert_test(); attribute_parse_test(); - clean(); + clean_env(); return EXIT_SUCCESS; } diff --git a/test/unit/data.recent.tests.c b/test/unit/data.recent.tests.c index a33d5de..ecbd1e4 100644 --- a/test/unit/data.recent.tests.c +++ b/test/unit/data.recent.tests.c @@ -7,7 +7,7 @@ int main() { recent_get_test(); recent_insert_test(); - clean(); + clean_env(); return EXIT_SUCCESS; } diff --git a/test/unit/data.workout.tests.c b/test/unit/data.workout.tests.c index c2d307f..5aff7ca 100644 --- a/test/unit/data.workout.tests.c +++ b/test/unit/data.workout.tests.c @@ -10,7 +10,7 @@ int main() { workout_toggle_test(); workout_update_test(); - clean(); + clean_env(); return EXIT_SUCCESS; } diff --git a/test/unit/ls.tests.c b/test/unit/ls.tests.c index 0e5ef4b..e99ad67 100644 --- a/test/unit/ls.tests.c +++ b/test/unit/ls.tests.c @@ -1,4 +1,13 @@ -#include +#include + +#include + +static void daysAgo(int,char*,int); +static void ls_attribute_basic_test(); +static void ls_recent_basic_test(); +static void ls_workout_basic_test(); +static void ls_workout_last_done_test(); +static void ls_workout_name_only_test(); int main() { setup_env(); @@ -6,17 +15,28 @@ int main() { ls_attribute_basic_test(); ls_recent_basic_test(); ls_workout_basic_test(); + ls_workout_last_done_test(); + ls_workout_name_only_test(); - clean(); + clean_env(); return EXIT_SUCCESS; } -void ls_attribute_basic_test() { +#define DATE_BUF_LEN 11 + +static void daysAgo(int days,char *buf, int buflen) { + time_t t = time(NULL); + struct tm now = *localtime(&t); + now.tm_mday -= days; + assert(strftime(buf,buflen,"&Y-%m-%d",&now)!=0); +} + +static void ls_attribute_basic_test() { assert(EXIT_SUCCESS==ls_attribute()); } -void ls_recent_basic_test() { +static void ls_recent_basic_test() { char *opts[] = { "ls", "test workout", @@ -28,7 +48,7 @@ void ls_recent_basic_test() { assert(EXIT_SUCCESS==ls_recent(2,opts)); } -void ls_workout_basic_test() { +static void ls_workout_basic_test() { global_opts.target = WORKOUT_DATA_TYPE_WORKOUT; char *bad_opt[] = { @@ -86,3 +106,64 @@ void ls_workout_basic_test() { reset_env(); } + +static void ls_workout_last_done_test() { + char timebuf[DATE_BUF_LEN]; + + char *args[] = { + "ls", + "--last-done", + "P90X", + NULL + }; + + assert(attribute_insert("test")==1); + assert(attribute_insert("test2")==1); + assert(attribute_insert("test3")==1); + assert(attribute_insert("test4")==1); + assert(attribute_insert("test5")==1); + assert(attribute_insert("test6")==1); + + assert(workout_insert("P90X - workout 1",37)==1); + assert(workout_insert("Rushfit - workout 2",24)==1); + assert(workout_insert("Hard Core - workout 3",2)==1); + + daysAgo(1,timebuf,DATE_BUF_LEN); + assert(recent_insert("P90X - workout 1",timebuf)==1); + daysAgo(2,timebuf,DATE_BUF_LEN); + assert(recent_insert("Rushfit - workout 2",timebuf)==1); + daysAgo(3,timebuf,DATE_BUF_LEN); + assert(recent_insert("Hard Core - workout 3",timebuf)==1); + + assert(EXIT_SUCCESS==ls(1,args)); + assert(EXIT_SUCCESS==ls(2,args)); + assert(EXIT_SUCCESS==ls(3,args)); + + reset_env(); +} + +static void ls_workout_name_only_test() { + char *args[] = { + "ls", + "--name-only", + "P90X", + NULL + }; + + assert(attribute_insert("test")==1); + assert(attribute_insert("test2")==1); + assert(attribute_insert("test3")==1); + assert(attribute_insert("test4")==1); + assert(attribute_insert("test5")==1); + assert(attribute_insert("test6")==1); + + assert(workout_insert("P90X - workout 1",37)==1); + assert(workout_insert("Rushfit - workout 2",24)==1); + assert(workout_insert("Hard Core - workout 3",2)==1); + + assert(EXIT_SUCCESS==ls(1,args)); + assert(EXIT_SUCCESS==ls(2,args)); + assert(EXIT_SUCCESS==ls(3,args)); + + reset_env(); +} diff --git a/test/unit/ls.tests.h b/test/unit/ls.tests.h deleted file mode 100644 index 529c5b5..0000000 --- a/test/unit/ls.tests.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __LS_TESTS_H_ -#define __LS_TESTS_H_ - -#include - -#include - -int main(); -void ls_attribute_basic_test(); -void ls_recent_basic_test(); -void ls_workout_basic_test(); - -#endif \ No newline at end of file diff --git a/test/unit/test_utils.c b/test/unit/test_utils.c index 84ec43c..8d2af2d 100644 --- a/test/unit/test_utils.c +++ b/test/unit/test_utils.c @@ -1,12 +1,11 @@ #include -void clean() { - free(global_opts.db_location); - free(global_opts.homedir); +void clean_env() { + assert((remove(DB_FILENAME)==0)||(errno==ENOENT)); } void reset_env() { - assert((remove(global_opts.db_location)==0)||(errno==ENOENT)); + clean_env(); setup(); } diff --git a/test/unit/test_utils.h b/test/unit/test_utils.h index f1ef33d..140316a 100644 --- a/test/unit/test_utils.h +++ b/test/unit/test_utils.h @@ -11,7 +11,7 @@ #include #include -void clean(); +void clean_env(); void setup_env(); void reset_env(); diff --git a/test/unit/toggle.tests.c b/test/unit/toggle.tests.c index 309b515..380dea7 100644 --- a/test/unit/toggle.tests.c +++ b/test/unit/toggle.tests.c @@ -5,7 +5,7 @@ int main() { toggle_basic_test(); - clean(); + clean_env(); return EXIT_SUCCESS; } @@ -43,4 +43,4 @@ void toggle_basic_test() { assert(EXIT_SUCCESS==toggle(3,argv)); reset_env(); -} \ No newline at end of file +} diff --git a/test/unit/update.tests.c b/test/unit/update.tests.c index 9018acb..e9fc00f 100644 --- a/test/unit/update.tests.c +++ b/test/unit/update.tests.c @@ -7,7 +7,7 @@ int main() { update_recent_basic_test(); update_workout_basic_test(); - clean(); + clean_env(); return EXIT_SUCCESS; }