From: alex Date: Mon, 2 Jan 2023 08:08:16 +0000 (-0800) Subject: Bugfixes X-Git-Tag: v1.0.1^0 X-Git-Url: http://git.infiniteadaptability.org/?a=commitdiff_plain;h=1ac4b76c722fb3764a3a366663ae689030a1b49e;p=events Bugfixes Fixed recur across year/month boundaries not working properly Improved timezone handling Fixed prune not re-adding recurring events Refactored recur functionality into own function --- diff --git a/Makefile.am b/Makefile.am index 4ad2767..7fafde0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,9 +39,11 @@ ev_SOURCES = \ src/opt/until.c \ src/postpone.c \ src/prune.c \ + src/recur.c \ src/rm.c \ src/seek.c \ - src/usage.c + src/usage.c \ + src/validate.c ev_SOURCES += \ inc/add.h \ @@ -56,9 +58,11 @@ ev_SOURCES += \ inc/opt.h \ inc/postpone.h \ inc/prune.h \ + inc/recur.h \ inc/rm.h \ inc/seek.h \ - inc/usage.h + inc/usage.h \ + inc/validate.h man_MANS = man/ev.1 diff --git a/completions/ev b/completions/ev index 96991f8..aa2f9d8 100644 --- a/completions/ev +++ b/completions/ev @@ -1,7 +1,7 @@ _ev() { IFS=$'\n' COMPREPLY=() - local commands=$'add\ndismiss\nls\npostpone\nprune\nrm' + local commands=$'add\ndismiss\nls\npostpone\nprune\nrm\nvalidate' declare -A options options[--all]="--all" @@ -76,6 +76,7 @@ _ev() { echo "$rmformat" COMPREPLY=() ;; + validate) ;; ev) COMPREPLY=($(compgen -W "${commands}" -- "$2")) COMPREPLY+=($(compgen -W "${opts}" -- "$2")) @@ -88,6 +89,6 @@ _ev() { COMPREPLY=() ;; esac -} >> /tmp/ev.output +} complete -F _ev ev diff --git a/configure.ac b/configure.ac index 1461b9d..ed5a483 100644 --- a/configure.ac +++ b/configure.ac @@ -50,7 +50,7 @@ AC_PROG_INSTALL # Checks for libraries. # Checks for header files. -AC_CHECK_HEADERS([limits.h stdlib.h string.h unistd.h]) +AC_CHECK_HEADERS([limits.h stdlib.h string.h time.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T diff --git a/inc/args.h b/inc/args.h index e518298..ebda10b 100644 --- a/inc/args.h +++ b/inc/args.h @@ -13,7 +13,8 @@ enum sub_command { SUB_COMMAND_LS, SUB_COMMAND_POSTPONE, SUB_COMMAND_PRUNE, - SUB_COMMAND_RM + SUB_COMMAND_RM, + SUB_COMMAND_VALIDATE }; int args(int,char**); diff --git a/inc/event.h b/inc/event.h index 87670d9..390253f 100644 --- a/inc/event.h +++ b/inc/event.h @@ -58,6 +58,7 @@ int event_parse(char*, size_t, struct event*); int event_place_set(struct event*, const char*); int event_serialize(char*, size_t, const struct event*); int event_time_compare(const struct tm*,time_t); +int event_time_date_only(const struct tm*); int event_time_set(struct tm*, const char*); int event_within(struct event*, struct event_filter*); diff --git a/inc/main.h b/inc/main.h index 7cca03e..35304ae 100644 --- a/inc/main.h +++ b/inc/main.h @@ -11,6 +11,7 @@ #include #include #include +#include int main(int,char**); diff --git a/inc/prune.h b/inc/prune.h index f45aacc..73ad379 100644 --- a/inc/prune.h +++ b/inc/prune.h @@ -5,6 +5,7 @@ #include #include +#include #include int prune(); diff --git a/inc/recur.h b/inc/recur.h new file mode 100644 index 0000000..9c7710a --- /dev/null +++ b/inc/recur.h @@ -0,0 +1,8 @@ +#ifndef __RECUR_H_ +#define __RECUR_H_ + +#include + +int recur(struct event*); + +#endif diff --git a/inc/rm.h b/inc/rm.h index d93856e..bf5e4e5 100644 --- a/inc/rm.h +++ b/inc/rm.h @@ -9,6 +9,7 @@ #include #include #include +#include #define RM_FLAG_DISMISS 1 diff --git a/inc/validate.h b/inc/validate.h new file mode 100644 index 0000000..ae54922 --- /dev/null +++ b/inc/validate.h @@ -0,0 +1,11 @@ +#ifndef __VALIDATE_H_ +#define __VALIDATE_H_ + +#include +#include + +#include + +int validate(); + +#endif diff --git a/man/ev.1 b/man/ev.1 index 53c1953..1ceb378 100644 --- a/man/ev.1 +++ b/man/ev.1 @@ -1,4 +1,4 @@ -.TH EV "1" "2022 September 13" "v0.0.0" "Events" +.TH EV "1" "2022 September 13" "v1.0.0" "Events" .SH NAME ev \- human readable events organizer @@ -37,6 +37,8 @@ If no COMMAND is specified, COMMAND defaults to ls. .B prune .IP \(bu .B rm +.IP \(bu +.B validate .SH OPTIONS .TP @@ -167,6 +169,11 @@ postpones event in the 0th position by 1 day .B ev postpone 0 +.TP +validate events file format +.B +ev validate + .SH "SEE ALSO" .BR git (1) diff --git a/src/args.c b/src/args.c index 2fc32e3..0efdb5f 100644 --- a/src/args.c +++ b/src/args.c @@ -70,6 +70,7 @@ enum sub_command args(int argc, char **argv) { if(strcmp(argv[optind],"postpone")==0) { return SUB_COMMAND_POSTPONE; } if(strcmp(argv[optind],"prune")==0) { return SUB_COMMAND_PRUNE; } if(strcmp(argv[optind],"rm")==0) { return SUB_COMMAND_RM; } + if(strcmp(argv[optind],"validate")==0) { return SUB_COMMAND_VALIDATE; } goto fail; fail: diff --git a/src/event/parse.c b/src/event/parse.c index b1cf3d1..58d6a07 100644 --- a/src/event/parse.c +++ b/src/event/parse.c @@ -28,6 +28,8 @@ static int event_parse_name(char *buf, size_t buf_size, struct event *ev) { token = normal; } + if(strlen(buf)<1) { return -1; } + p = strtok(buf,token); if(p!=NULL) { if(event_name_set(ev,p)<0) { return -1; } @@ -127,7 +129,11 @@ static int event_parse_time(char *buf, size_t buf_size, struct event *ev) { if(event_time_set(&(ev->datetime),buf)<0) { return -1; } - memmove(buf,p,(buf_size - (p - buf))); + if((buf_size - (p - buf))>0) { + memmove(buf,p,(buf_size - (p - buf))); + } else { + buf[0] = '\0'; + } return 1; } diff --git a/src/event/serial.c b/src/event/serial.c index df20225..64381c7 100644 --- a/src/event/serial.c +++ b/src/event/serial.c @@ -97,13 +97,13 @@ static int event_serialize_place(char *buf, size_t buf_size, const struct event } static int event_serialize_time(char *buf, size_t buf_size, const struct tm *datetime) { - size_t len; + size_t len, i; assert(buf!=NULL); len = strlen(buf); - if(datetime->tm_sec==0 && datetime->tm_min==0 && datetime->tm_hour == 0) { + if(event_time_date_only(datetime)>0) { if(0==strftime( &(buf[len]), /* char *s */ buf_size - len, /* size_t max */ @@ -111,12 +111,18 @@ static int event_serialize_time(char *buf, size_t buf_size, const struct tm *dat datetime /* const struct tm *tm */ )) { return -1; } } else { - if(0==strftime( + long offset = datetime->__tm_gmtoff; + + i = strftime( &(buf[len]), /* char *s */ buf_size - len, /* size_t max */ - "%Y-%m-%d %H:%M:%S %z", /* const char *format */ + "%Y-%m-%d %H:%M:%S ", /* const char *format */ datetime /* const struct tm *tm */ - )) { return -1; } + ); + if(i==0) { return -1; } + + len += i; + if(snprintf(&(buf[len]),buf_size - len,"%+.2li%.2li", offset/3600, offset%60)<0) { return -1; } } return 1; diff --git a/src/event/time.c b/src/event/time.c index 1a46aaf..0245b44 100644 --- a/src/event/time.c +++ b/src/event/time.c @@ -14,25 +14,38 @@ int event_time_compare(const struct tm *datetime, time_t time) { return 0; } +int event_time_date_only(const struct tm *date) { + if((*date).tm_sec != 0) { return -1; } + if((*date).tm_min != 0) { return -1; } + if((*date).tm_hour != 0) { return -1; } + return 1; +} + int event_time_set(struct tm *datetime, const char *str) { const char *p; - - p = str; + + /* this call is necessary here to guarantee + * that extern int daylight is set correctly. + * see man tzset. + */ + //tzset(); memset(datetime, 0, sizeof(struct tm)); + datetime->tm_isdst = -1; /* minimum expected date format */ - p = strptime(p,"%Y-%m-%d",datetime); - if(NULL==p) { return -1; } - - /* attempt to get time information */ - p = strptime(p,"%H:%M:%S",datetime); + p = strptime(str,"%Y-%m-%d %H:%M:%S %z",datetime); + if(p!=NULL) { return 1; } - /* attempt to get timezone information */ + p = strptime(str,"%Y-%m-%d %H:%M:%S",datetime); if(p!=NULL) { - strptime(p,"%z",datetime); + if(mktime(datetime)<0) { return -1; } + return 1; } + p = strptime(str,"%Y-%m-%d",datetime); + if(NULL==p) { return -1; } + return 1; } diff --git a/src/ls.c b/src/ls.c index 52564f6..480dc73 100644 --- a/src/ls.c +++ b/src/ls.c @@ -35,19 +35,26 @@ close: } static int event_print(struct event *ev) { + char datebuf[20]; + time_t timestamp; + struct tm *local; + long offset; + assert(ev!=NULL); if(NULL==ev->name) { return -1; } - char datebuf[20]; - if( - (ev->datetime.tm_sec==0) && - (ev->datetime.tm_min == 0) && - (ev->datetime.tm_hour == 0) - ) { + if(event_time_date_only(&(ev->datetime))>0) { if(strftime(datebuf,20,"%Y-%m-%d",&(ev->datetime))!=10) { return -1; } } else { - if(strftime(datebuf,20,"%Y-%m-%d %H:%M:%S",&(ev->datetime))!=19) { return -1; } + offset = ev->datetime.tm_gmtoff; + timestamp = timegm(&(ev->datetime)) - offset; + if(timestamp<0) { return -1; } + + local = localtime(×tamp); + if(NULL==local) { return -1; } + + if(strftime(datebuf,20,"%Y-%m-%d %H:%M:%S",local)!=19) { return -1; } } if(fprintf(stdout,"%-20s\t",datebuf)<0) { return -1; } diff --git a/src/main.c b/src/main.c index 52bf386..bd3f4e5 100644 --- a/src/main.c +++ b/src/main.c @@ -22,6 +22,8 @@ int main(int argc, char **argv) { return prune(); case SUB_COMMAND_RM: return rm(argc,argv,0); + case SUB_COMMAND_VALIDATE: + return validate(); default: return EXIT_FAILURE; } diff --git a/src/prune.c b/src/prune.c index 0c3bc44..422f5f8 100644 --- a/src/prune.c +++ b/src/prune.c @@ -1,5 +1,43 @@ #include +static int copy_recur_events(FILE*,FILE*); + +static int copy_recur_events(FILE *src, FILE *to) { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + time_t now; + ssize_t i; + + now = time(NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + while((i = file_line_next(buf,EVENT_SERIALIZE_MAX_LENGTH,src))>0) { + if(event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { return -1; } + + if(event_time_compare(&(ev.datetime),now)>0) { + event_free(&ev); + if(fseek(src,-i,SEEK_CUR)!=0) { return -1; } + break; + } + + switch(recur(&ev)) { + case -1: + event_free(&ev); + return -1; + case 1: + if(event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { return -1; } + if(fwrite(buf,1,i,to)!=i) { return -1; } + break; + default: + event_free(&ev); + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + break; + } + } + + return 1; +} + int prune() { FILE *src, *to; @@ -10,8 +48,8 @@ int prune() { src = file_open(); if(NULL==src) { return EXIT_FAILURE; } - if(seek(src,time(NULL))<0) { return EXIT_FAILURE; } - + if(copy_recur_events(src,to)<0) { return EXIT_FAILURE; } + if(file_pipe(to,src)<0) { return EXIT_FAILURE; } if(fclose(src)!=0) { return EXIT_FAILURE; } diff --git a/src/recur.c b/src/recur.c new file mode 100644 index 0000000..8aec402 --- /dev/null +++ b/src/recur.c @@ -0,0 +1,54 @@ +#include + +int recur(struct event *ev) { + time_t compare; + struct tm tm; + + if(ev->options.recur==0) { return 0; } + + memcpy(&tm,&(ev->datetime),sizeof(struct tm)); + + switch(ev->options.recur_period) { + case TIME_PERIOD_YEAR: + tm.tm_year += ev->options.recur; + break; + case TIME_PERIOD_MONTH: + tm.tm_mon += ev->options.recur; + break; + case TIME_PERIOD_WEEK: + tm.tm_mday += 7*(ev->options.recur); + break; + case TIME_PERIOD_DAY: + tm.tm_mday += ev->options.recur; + break; + case TIME_PERIOD_HOUR: + tm.tm_hour += ev->options.recur; + break; + case TIME_PERIOD_MINUTE: + tm.tm_min += ev->options.recur; + break; + case TIME_PERIOD_SECOND: + tm.tm_sec += ev->options.recur; + break; + default: + return -1; + } + + tm.tm_isdst = -1; + compare = mktime(&tm); + if(compare<0) { return -1; } + + ev->datetime.tm_year = tm.tm_year; + ev->datetime.tm_mon = tm.tm_mon; + ev->datetime.tm_mday = tm.tm_mday; + ev->datetime.tm_hour = tm.tm_hour; + ev->datetime.tm_min = tm.tm_min; + ev->datetime.tm_sec = tm.tm_sec; + ev->datetime.tm_wday = tm.tm_wday; + + if(event_time_compare(&(ev->options.until),0)>0) { + if(event_time_compare(&(ev->options.until),compare)<0) { return 0; } + } + + return 1; +} diff --git a/src/rm.c b/src/rm.c index 819996f..29adea4 100644 --- a/src/rm.c +++ b/src/rm.c @@ -6,42 +6,16 @@ static int handle_args(int,char**,unsigned long int*, time_t *); static int check_need_readd(struct event *ev) { char buf[EVENT_SERIALIZE_MAX_LENGTH]; char timebuf[40]; - struct tm tm; assert(ev!=NULL); - if(ev->options.recur==0) { return 1; } - - - switch(ev->options.recur_period) { - case TIME_PERIOD_YEAR: - ev->datetime.tm_year += ev->options.recur; - break; - case TIME_PERIOD_MONTH: - ev->datetime.tm_mon += ev->options.recur; - break; - case TIME_PERIOD_WEEK: - ev->datetime.tm_mday += 7*(ev->options.recur); - break; - case TIME_PERIOD_DAY: - ev->datetime.tm_mday += ev->options.recur; - break; - case TIME_PERIOD_HOUR: - ev->datetime.tm_hour += ev->options.recur; - break; - case TIME_PERIOD_MINUTE: - ev->datetime.tm_min += ev->options.recur; - break; - case TIME_PERIOD_SECOND: - ev->datetime.tm_sec += ev->options.recur; - break; - default: + switch(recur(ev)) { + case -1: return -1; - } - - memcpy(&tm,&(ev->datetime),sizeof(struct tm)); - if(event_time_compare(&(ev->options.until),0)>0) { - if(event_time_compare(&(ev->options.until),mktime(&tm))<0) { return 1; } + case 0: + return 1; + default: + break; } strftime(timebuf,40,"%a, %d %b %Y %T %z",&(ev->datetime)); diff --git a/src/validate.c b/src/validate.c new file mode 100644 index 0000000..e0025e9 --- /dev/null +++ b/src/validate.c @@ -0,0 +1,37 @@ +#include + +int validate() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + FILE *src; + ssize_t i; + size_t line; + + src = file_open(); + if(NULL==src) { return EXIT_FAILURE; } + + line = 1; + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + while((i = file_line_next(buf,EVENT_SERIALIZE_MAX_LENGTH,src))>0) { + if(event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { + fprintf(stderr,"invalid event: event on line %lu failed to parse\n",line); + return EXIT_FAILURE; + } + + event_free(&ev); + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + + line++; + } + + if(i<0) { + if(ferror(src)!=0) { + fprintf(stderr,"failed to get line"); + return EXIT_FAILURE; + } + } + + fprintf(stdout,"%s: \x1B[32mVALID\x1B[0m\n",global_options.file); + + return EXIT_SUCCESS; +} diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am index ba94b39..59267cd 100644 --- a/test/unit/Makefile.am +++ b/test/unit/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = \ test_macros.h \ test_utils.h -check_PROGRAMS = add.tests args.tests copy.tests cut.tests event.tests file.tests ls.tests opt.tests postpone.tests prune.tests rm.tests seek.tests usage.tests +check_PROGRAMS = add.tests args.tests copy.tests cut.tests event.tests file.tests ls.tests opt.tests postpone.tests prune.tests recur.tests rm.tests seek.tests usage.tests validate.tests TESTS = $(check_PROGRAMS) if ENABLE_MEMCHECK @@ -96,7 +96,8 @@ ls_tests_SOURCES = \ $(event_SOURCES) \ ls.tests.c \ $(top_srcdir)/src/file/line.c \ - $(top_srcdir)/src/file/open.c + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/tmp.c ls_tests_CPPFLAGS = $(AM_CPPFLAGS) \ -DLS_SRC_FILE="$(top_srcdir)/src/ls.c" @@ -138,8 +139,15 @@ prune_tests_SOURCES = \ $(top_srcdir)/src/file/pipe.c \ $(top_srcdir)/src/file/tmp.c \ $(top_srcdir)/src/prune.c \ + $(top_srcdir)/src/recur.c \ $(top_srcdir)/src/seek.c +recur_tests_SOURCES = \ + $(common_SOURCES) \ + $(event_SOURCES) \ + recur.tests.c \ + $(top_srcdir)/src/recur.c + rm_tests_SOURCES = \ $(common_SOURCES) \ $(event_SOURCES) \ @@ -153,6 +161,7 @@ rm_tests_SOURCES = \ $(top_srcdir)/src/file/pipe.c \ $(top_srcdir)/src/file/tmp.c \ $(top_srcdir)/src/opt/filter.c \ + $(top_srcdir)/src/recur.c \ $(top_srcdir)/src/usage.c rm_tests_CPPFLAGS = $(AM_CPPFLAGS) \ @@ -177,3 +186,11 @@ usage_tests_SOURCES = \ usage_tests_CPPFLAGS = $(AM_CPPFLAGS) \ -DARGS_SRC_FILE="$(top_srcdir)/src/args.c" \ -DUSAGE_SRC_FILE="$(top_srcdir)/src/usage.c" + +validate_tests_SOURCES = \ + $(common_SOURCES) \ + $(event_SOURCES) \ + validate.tests.c \ + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/validate.c diff --git a/test/unit/add.tests.c b/test/unit/add.tests.c index b34370a..14db545 100644 --- a/test/unit/add.tests.c +++ b/test/unit/add.tests.c @@ -71,7 +71,7 @@ static void add_time_set_basic_test() { assert(1==add_time_set(&ev,"2022-09-01")); - assert(mktime(&(ev.datetime))==1662019200); + assert(mktime(&(ev.datetime))==1662015600); } static void add_to_file_basic_test() { @@ -126,7 +126,7 @@ static void add_to_file_basic_test() { memset(buf,0,1000); assert(fread(buf,1,1000,fp)==73); - assert(strcmp(buf,"2022-06-01 20:00:00 +0000:event3:place\n2022-08-01:name\n2022-09-01:event2\n")==0); + assert(strcmp(buf,"2022-06-01 20:00:00 -0700:event3:place\n2022-08-01:name\n2022-09-01:event2\n")==0); assert(fclose(fp)==0); diff --git a/test/unit/args.tests.c b/test/unit/args.tests.c index 41da3da..ea2a67d 100644 --- a/test/unit/args.tests.c +++ b/test/unit/args.tests.c @@ -19,6 +19,7 @@ static void args_postpone_basic_test(); static void args_prune_basic_test(); static void args_rm_basic_test(); static void args_short_opts_basic_test(); +static void args_validate_basic_test(); int main() { setup_env(); @@ -57,6 +58,7 @@ static void args_basic_test() { args_postpone_basic_test(); args_prune_basic_test(); args_rm_basic_test(); + args_validate_basic_test(); args_short_opts_basic_test(); args_long_opts_basic_test(); @@ -154,7 +156,7 @@ static void args_ls_end_date_basic_test() { }; assert(SUB_COMMAND_LS==args(4,ls_end_date_short_args)); assert(global_options.event_filter.start==0); - assert(global_options.event_filter.end==1114934400); + assert(global_options.event_filter.end==1114930800); optind = 0; reset_env(); @@ -389,3 +391,15 @@ static void args_short_opts_basic_test() { reset_env(); } + +static void args_validate_basic_test() { + char *validate_args[] = { + "ev", + "validate", + NULL + }; + + assert(SUB_COMMAND_VALIDATE==args(2,validate_args)); + + reset_env(); +} diff --git a/test/unit/event.tests.c b/test/unit/event.tests.c index 09ef926..9fb161e 100644 --- a/test/unit/event.tests.c +++ b/test/unit/event.tests.c @@ -6,6 +6,7 @@ int main(); static void event_init_basic_test(); static void event_name_set_basic_test(); static void event_parse_basic_test(); +static void event_parse_missing_name_test(); static void event_place_set_basic_test(); static void event_serialize_basic_test(); static void event_serialize_parse_exactness_test(); @@ -21,6 +22,7 @@ int main() { event_time_compare_basic_test(); event_name_set_basic_test(); event_parse_basic_test(); + event_parse_missing_name_test(); event_place_set_basic_test(); event_serialize_basic_test(); event_serialize_parse_exactness_test(); @@ -75,7 +77,7 @@ static void event_parse_basic_test() { time_unset(&(ev1.options.until)); assert(strcmp(ev1.name,"test event")==0); assert(strcmp(ev1.place,"test place")==0); - assert(mktime(&(ev1.datetime))==1662019200); + assert(mktime(&(ev1.datetime))==1662015600); event_init(&ev2); char ex2[] = "2022-09-02:\"test: event2\":\"test: place2\""; @@ -85,7 +87,7 @@ static void event_parse_basic_test() { time_unset(&(ev2.options.until)); assert(strcmp(ev2.name,"test: event2")==0); assert(strcmp(ev2.place,"test: place2")==0); - assert(mktime(&(ev2.datetime))==1662105600); + assert(mktime(&(ev2.datetime))==1662102000); event_init(&ev3); char ex3[] = "2022-09-03 09:30:00:test event3:test place3"; @@ -95,7 +97,7 @@ static void event_parse_basic_test() { time_unset(&(ev3.options.until)); assert(strcmp(ev3.name,"test event3")==0); assert(strcmp(ev3.place,"test place3")==0); - assert(mktime(&(ev3.datetime))==1662226200); + assert(mktime(&(ev3.datetime))==1662222600); event_init(&ev4); char ex4[] = "2022-09-04 09:30:00 -0700:test event4:test place4"; @@ -105,7 +107,7 @@ static void event_parse_basic_test() { time_unset(&(ev4.options.until)); assert(strcmp(ev4.name,"test event4")==0); assert(strcmp(ev4.place,"test place4")==0); - assert(mktime(&(ev4.datetime))==1662312600); + assert(mktime(&(ev4.datetime))==1662309000); event_init(&ev5); char ex5[] = "[recur:1M]2022-08-04:test event5:test place5"; @@ -115,7 +117,7 @@ static void event_parse_basic_test() { time_unset(&(ev5.options.until)); assert(strcmp(ev5.name,"test event5")==0); assert(strcmp(ev5.place,"test place5")==0); - assert(mktime(&(ev5.datetime))==1659600000); + assert(mktime(&(ev5.datetime))==1659596400); event_init(&ev6); char ex6[] = "[recur:1W]2022-06-05 09:30:00 -0700:test event6:test place6"; @@ -125,27 +127,27 @@ static void event_parse_basic_test() { time_unset(&(ev6.options.until)); assert(strcmp(ev6.name,"test event6")==0); assert(strcmp(ev6.place,"test place6")==0); - assert(mktime(&(ev6.datetime))==1654450200); + assert(mktime(&(ev6.datetime))==1654446600); event_init(&ev7); char ex7[] = "[recur:1W,until:2022-11-01]2022-10-01 09:30:00 -0700:test event7:test place7"; assert(event_parse(ex7,sizeof(ex7),&ev7)==1); assert(ev7.options.recur==1); assert(ev7.options.recur_period==TIME_PERIOD_WEEK); - assert(mktime(&(ev7.options.until))==1667289600); + assert(mktime(&(ev7.options.until))==1667286000); assert(strcmp(ev7.name,"test event7")==0); assert(strcmp(ev7.place,"test place7")==0); - assert(mktime(&(ev7.datetime))==1664645400); + assert(mktime(&(ev7.datetime))==1664641800); event_init(&ev8); char ex8[] = "[until:2022-10-10,recur:1W]2022-10-01 09:30:00 -0700:test event8:test place8"; assert(event_parse(ex8,sizeof(ex8),&ev8)==1); assert(ev8.options.recur==1); assert(ev8.options.recur_period==TIME_PERIOD_WEEK); - assert(mktime(&(ev8.options.until))==1665388800); + assert(mktime(&(ev8.options.until))==1665385200); assert(strcmp(ev8.name,"test event8")==0); assert(strcmp(ev8.place,"test place8")==0); - assert(mktime(&(ev8.datetime))==1664645400); + assert(mktime(&(ev8.datetime))==1664641800); event_init(&ev9); char ex9[] = "2022-08-01:test"; @@ -155,7 +157,7 @@ static void event_parse_basic_test() { time_unset(&(ev9.options.until)); assert(strcmp(ev9.name,"test")==0); assert(NULL==ev9.place); - assert(mktime(&(ev9.datetime))==1659340800); + assert(mktime(&(ev9.datetime))==1659337200); event_free(&ev1); event_free(&ev2); @@ -168,6 +170,16 @@ static void event_parse_basic_test() { event_free(&ev9); } +static void event_parse_missing_name_test() { + struct event ev; + + event_init(&ev); + char ex[] = "2022-08-01"; + assert(event_parse(ex,sizeof(ex),&ev)==-1); + + event_free(&ev); +} + static void event_place_set_basic_test() { struct event ev; @@ -205,11 +217,18 @@ static void event_serialize_basic_test() { assert(strcmp(buf,"2022-07-01 08:01:15 -0100:event:place\n")==0); event_init(&ev); - assert(1==event_time_set(&(ev.datetime),"2022-07-01 08:01:15 -0100")); + assert(1==event_time_set(&(ev.datetime),"2022-07-01 08:01:15 -0700")); assert(1==event_name_set(&ev,"event: what?")); assert(1==event_place_set(&ev,"place: here")); assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); - assert(strcmp(buf,"2022-07-01 08:01:15 -0100:\"event: what?\":\"place: here\"\n")==0); + assert(strcmp(buf,"2022-07-01 08:01:15 -0700:\"event: what?\":\"place: here\"\n")==0); + + event_init(&ev); + assert(1==event_time_set(&(ev.datetime),"2022-07-01 08:01:15 +2300")); + assert(1==event_name_set(&ev,"event: what?")); + assert(1==event_place_set(&ev,"place: here")); + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + assert(strcmp(buf,"2022-07-01 08:01:15 +2300:\"event: what?\":\"place: here\"\n")==0); event_init(&ev); assert(1==event_time_set(&(ev.datetime),"2022-06-21")); @@ -262,7 +281,7 @@ static void event_time_compare_basic_test() { char timebuf[40]; struct event ev; time_t timet; - struct tm *now, got; + struct tm *now; event_init(&ev); @@ -272,11 +291,7 @@ static void event_time_compare_basic_test() { assert(strftime(timebuf,40,"%Y-%m-%d %H:%M:%S %z",now)==25); assert(1==event_time_set(&(ev.datetime),timebuf)); - /* daylight savings time set here for some reason. - * resetting timet to be time set in ev... - */ - memcpy(&got,&(ev.datetime),sizeof(struct tm)); - timet = mktime(&got); + timet = mktime(&(ev.datetime)); timet -= 1000; assert(1==event_time_compare(&(ev.datetime),timet)); @@ -309,8 +324,6 @@ static void event_time_set_basic_test() { assert(ev.datetime.tm_year==99); assert(ev.datetime.tm_wday==0); assert(ev.datetime.tm_yday==58); - assert(ev.datetime.tm_isdst==0); - assert(ev.datetime.tm_gmtoff==0); assert(1==event_time_set(&(ev.datetime),"1997-01-28 not a time")); assert(ev.datetime.tm_sec==0); @@ -321,8 +334,6 @@ static void event_time_set_basic_test() { assert(ev.datetime.tm_year==97); assert(ev.datetime.tm_wday==2); assert(ev.datetime.tm_yday==27); - assert(ev.datetime.tm_isdst==0); - assert(ev.datetime.tm_gmtoff==0); assert(1==event_time_set(&(ev.datetime),"2002-05-24 121212")); assert(ev.datetime.tm_sec==0); @@ -333,8 +344,6 @@ static void event_time_set_basic_test() { assert(ev.datetime.tm_year==102); assert(ev.datetime.tm_wday==5); assert(ev.datetime.tm_yday==143); - assert(ev.datetime.tm_isdst==0); - assert(ev.datetime.tm_gmtoff==0); assert(1==event_time_set(&(ev.datetime),"2012-12-22 12:1212")); assert(ev.datetime.tm_sec==0); @@ -345,8 +354,6 @@ static void event_time_set_basic_test() { assert(ev.datetime.tm_year==112); assert(ev.datetime.tm_wday==6); assert(ev.datetime.tm_yday==356); - assert(ev.datetime.tm_isdst==0); - assert(ev.datetime.tm_gmtoff==0); assert(1==event_time_set(&(ev.datetime),"2022-01-24 09:19:55")); assert(ev.datetime.tm_sec==55); @@ -357,8 +364,6 @@ static void event_time_set_basic_test() { assert(ev.datetime.tm_year==122); assert(ev.datetime.tm_wday==1); assert(ev.datetime.tm_yday==23); - assert(ev.datetime.tm_isdst==0); - assert(ev.datetime.tm_gmtoff==0); assert(1==event_time_set(&(ev.datetime),"2009-01-03 13:25:25 -0800")); assert(ev.datetime.tm_sec==25); @@ -369,8 +374,18 @@ static void event_time_set_basic_test() { assert(ev.datetime.tm_year==109); assert(ev.datetime.tm_wday==6); assert(ev.datetime.tm_yday==2); - assert(ev.datetime.tm_isdst==0); assert(ev.datetime.tm_gmtoff==-28800); + + assert(1==event_time_set(&(ev.datetime),"2008-02-07 04:25:16 -0300")); + assert(ev.datetime.tm_sec==16); + assert(ev.datetime.tm_min==25); + assert(ev.datetime.tm_hour==4); + assert(ev.datetime.tm_mday==7); + assert(ev.datetime.tm_mon==1); + assert(ev.datetime.tm_year==108); + assert(ev.datetime.tm_wday==4); + assert(ev.datetime.tm_yday==37); + assert(ev.datetime.tm_gmtoff==-10800); } static void time_unset(struct tm *tm) { diff --git a/test/unit/ls.tests.c b/test/unit/ls.tests.c index 61d4f93..951947a 100644 --- a/test/unit/ls.tests.c +++ b/test/unit/ls.tests.c @@ -6,9 +6,11 @@ int main(); static void event_print_basic_test(); +static void event_print_timezone_test(); int main() { event_print_basic_test(); + event_print_timezone_test(); return EXIT_SUCCESS; } @@ -26,3 +28,49 @@ static void event_print_basic_test() { event_free(&ev); } + +static void event_print_timezone_test() { + struct event ev; + char buf[200]; + FILE *fp; + + char tempfile[] = "/tmp/XXXXXX"; + fp = file_temp(tempfile); + assert(fp!=NULL); + assert(fclose(fp)==0); + + fp = NULL; + + event_init(&ev); + + assert(1==event_name_set(&ev,"correct timezone")); + assert(1==event_time_set(&(ev.datetime),"2022-07-01 09:39:34")); + + fp = freopen(tempfile,"w",stdout); + assert(fp!=NULL); + + assert(1==event_print(&ev)); + + event_free(&ev); + event_init(&ev); + + assert(1==event_name_set(&ev,"timezone test")); + assert(1==event_time_set(&(ev.datetime),"2022-08-01 20:30:15 -0300")); + + assert(1==event_print(&ev)); + event_free(&ev); + + assert(fclose(fp)==0); + + memset(buf,0,200); + + fp = NULL; + fp = fopen(tempfile,"r"); + assert(fp!=NULL); + + assert(fread(buf,1,200,fp)==111); + assert(memcmp(buf,"2022-07-01 09:39:34 \tcorrect timezone \x1B[31m[OVERDUE]\x1B[0m\n2022-08-01 16:30:15 \ttimezone test \x1B[31m[OVERDUE]\x1B[0m\n",111)==0); + + assert(fclose(fp)==0); + assert(remove(tempfile)==0); +} diff --git a/test/unit/opt.tests.c b/test/unit/opt.tests.c index 0124f01..74bbf17 100644 --- a/test/unit/opt.tests.c +++ b/test/unit/opt.tests.c @@ -31,7 +31,7 @@ static void opt_event_filter_date_set_basic_test() { assert(global_options.event_filter.start==1230969600); assert(1==opt_event_filter_date_set(&(global_options.event_filter.end),"2022-08-22")); - assert(global_options.event_filter.end==1661155200); + assert(global_options.event_filter.end==1661151600); reset_env(); } diff --git a/test/unit/postpone.tests.c b/test/unit/postpone.tests.c index 64a2d5f..886dbcc 100644 --- a/test/unit/postpone.tests.c +++ b/test/unit/postpone.tests.c @@ -54,7 +54,7 @@ static void handle_args_basic_test() { assert(handle_args(4,args_with_date,&offset,&start_time)==1); assert(offset==1); - assert(start_time==1585123200); + assert(start_time==1585119600); char *args_with_date_and_postpone[] = { "ev", @@ -67,7 +67,7 @@ static void handle_args_basic_test() { assert(handle_args(5,args_with_date_and_postpone,&offset,&start_time)==1); assert(offset==2); - assert(start_time==1585123200); + assert(start_time==1585119600); assert(strcmp(postpone_offset_string,"2W")==0); diff --git a/test/unit/prune.tests.c b/test/unit/prune.tests.c index 9d91c58..4bcce8e 100644 --- a/test/unit/prune.tests.c +++ b/test/unit/prune.tests.c @@ -4,11 +4,13 @@ int main(); static void prune_basic_test(); +static void prune_recur_test(); int main() { setup_env(); prune_basic_test(); + prune_recur_test(); clean_env(); @@ -69,3 +71,52 @@ static void prune_basic_test() { reset_env(); } + +static void prune_recur_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + char timebuf[40]; + struct tm curr, *now; + time_t now_timet; + FILE *src; + + now_timet = time(NULL); + now = localtime(&now_timet); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(timebuf,0,40); + + memcpy(&curr,now,sizeof(struct tm)); + curr.tm_year -= 1; + + assert(strftime(timebuf,20,"%Y-%m-%d",&curr)==10); + for(size_t i=0;i<10;i++) { + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + snprintf(buf,EVENT_SERIALIZE_MAX_LENGTH,"[recur:2Y]%s:test%lu\n",timebuf,i); + + assert(fwrite(buf,1,strlen(buf),src)==27); + } + + curr.tm_year += 2; + assert(strftime(timebuf,20,"%Y-%m-%d",&curr)==10); + + assert(fclose(src)==0); + + assert(prune()==EXIT_SUCCESS); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==270); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + sprintf(buf,"[recur:2Y]%s:test0\n[recur:2Y]%s:test1\n[recur:2Y]%s:test2\n[recur:2Y]%s:test3\n[recur:2Y]%s:test4\n[recur:2Y]%s:test5\n[recur:2Y]%s:test6\n[recur:2Y]%s:test7\n[recur:2Y]%s:test8\n[recur:2Y]%s:test9\n",timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf); + assert(memcmp(got,buf,170)==0); + + assert(fclose(src)==0); + + reset_env(); +} diff --git a/test/unit/recur.tests.c b/test/unit/recur.tests.c new file mode 100644 index 0000000..e744d60 --- /dev/null +++ b/test/unit/recur.tests.c @@ -0,0 +1,49 @@ +#include + +#include + +int main(); +static void recur_basic_test(); + +int main() { + setup_env(); + + recur_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void recur_basic_test() { + struct event ev; + struct tm *target; + time_t target_timet; + + event_init(&ev); + + assert(event_name_set(&ev,"test event")==1); + assert(event_time_set(&(ev.datetime),"2020-05-13")==1); + + assert(recur(&ev)==0); + + ev.options.recur = 1; + ev.options.recur_period = -1; + assert(recur(&ev)==-1); + + ev.options.recur_period = TIME_PERIOD_MONTH; + target_timet = 1585850345; // ~2020-04-02 + target = localtime(&target_timet); + memcpy(&(ev.options.until),target,sizeof(struct tm)); + + assert(recur(&ev)==0); + + memset(&(ev.options.until),0,sizeof(struct tm)); + assert(recur(&ev)==1); + + assert(1594623600==mktime(&(ev.datetime))); + + event_free(&ev); + + reset_env(); +} diff --git a/test/unit/rm.tests.c b/test/unit/rm.tests.c index 4c10ced..9e671dd 100644 --- a/test/unit/rm.tests.c +++ b/test/unit/rm.tests.c @@ -6,7 +6,9 @@ int main(); static void check_need_readd_basic_test(); +static void check_need_readd_month_boundary_test(); static void check_need_readd_until_test(); +static void check_need_readd_year_boundary_test(); static void handle_args_basic_test(); static void rm_basic_test(); static void rm_dismiss_basic_test(); @@ -18,7 +20,9 @@ int main() { rm_basic_test(); check_need_readd_basic_test(); + check_need_readd_month_boundary_test(); check_need_readd_until_test(); + check_need_readd_year_boundary_test(); rm_dismiss_basic_test(); clean_env(); @@ -68,6 +72,36 @@ static void check_need_readd_basic_test() { reset_env(); } +static void check_need_readd_month_boundary_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + FILE *src; + + event_init(&ev); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + assert(event_name_set(&ev,"test month boundary")==1); + assert(event_time_set(&(ev.datetime),"2021-01-30")==1); + + ev.options.recur = 1; + ev.options.recur_period = TIME_PERIOD_WEEK; + + assert(1==check_need_readd(&ev)); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + assert(fread(buf,1,EVENT_SERIALIZE_MAX_LENGTH,src)==41); + assert(memcmp(buf,"[recur:1W]2021-02-06:test month boundary\n",41)==0); + + assert(fclose(src)==0); + + reset_env(); +} + static void check_need_readd_until_test() { char buf[EVENT_SERIALIZE_MAX_LENGTH]; struct event ev; @@ -129,6 +163,36 @@ static void check_need_readd_until_test() { reset_env(); } +static void check_need_readd_year_boundary_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + FILE *src; + + event_init(&ev); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + assert(event_name_set(&ev,"test year boundary")==1); + assert(event_time_set(&(ev.datetime),"2019-01-15")==1); + + ev.options.recur = 1; + ev.options.recur_period = TIME_PERIOD_YEAR; + + assert(1==check_need_readd(&ev)); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + assert(fread(buf,1,EVENT_SERIALIZE_MAX_LENGTH,src)==40); + assert(memcmp(buf,"[recur:1Y]2020-01-15:test year boundary\n",40)==0); + + assert(fclose(src)==0); + + reset_env(); +} + static void handle_args_basic_test() { unsigned long int offset; time_t start_time; @@ -155,7 +219,7 @@ static void handle_args_basic_test() { assert(1==handle_args(4,args_with_date,&offset,&start_time)); assert(offset==2); - assert(start_time==1662019200); + assert(start_time==1662015600); } static void rm_basic_test() { diff --git a/test/unit/test_utils.c b/test/unit/test_utils.c index cccc21d..4f188a1 100644 --- a/test/unit/test_utils.c +++ b/test/unit/test_utils.c @@ -24,7 +24,7 @@ void reset_env() { } void setup_env() { - assert(setenv("TZ","America/Los_Angeles",1)==0); + assert(setenv("TZ",":America/Los_Angeles",1)==0); tzset(); srand(time(NULL)); diff --git a/test/unit/validate.tests.c b/test/unit/validate.tests.c new file mode 100644 index 0000000..5d0e4e0 --- /dev/null +++ b/test/unit/validate.tests.c @@ -0,0 +1,45 @@ +#include + +#include + +int main(); +static void validate_basic_test(); + +int main() { + setup_env(); + + validate_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void validate_basic_test(){ + char buf[1000]; + FILE *fp; + + memset(buf,0,1000); + + fp = fopen(global_options.file,"w"); + assert(fp!=NULL); + + strcpy(buf,"2022-08-01:test\n2022-08-02\n"); + assert(fwrite(buf,1,27,fp)==27); + assert(fclose(fp)==0); + + assert(validate()==EXIT_FAILURE); + + reset_env(); + + fp = fopen(global_options.file,"w"); + assert(fp!=NULL); + + strcpy(buf,"2022-08-01:test\n2022-08-02:test2\n"); + assert(fwrite(buf,1,33,fp)==33); + assert(fclose(fp)==0); + + assert(validate()==EXIT_SUCCESS); + + reset_env(); +}