From a064e6da6d9091178db93b273380dd82a88a03da Mon Sep 17 00:00:00 2001 From: Teemu Toivola Date: Fri, 12 Oct 2018 23:44:08 +0300 Subject: [PATCH] workaround and test for libgd/libgd#351 --- .gitignore | 1 + src/image.c | 47 ++++++++-- src/image.h | 1 + tests/dbsql_tests.c | 15 ---- tests/dbsql_tests.h | 1 - tests/image_tests.c | 201 +++++++++++++++++++++++++++++++++++++++++++ tests/vnstat_tests.c | 15 ++++ tests/vnstat_tests.h | 1 + 8 files changed, 258 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 5294ba9..71a2b66 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ vnstati *.trs coverage vnstat_tests +vnstati_check.png check_vnstat testdir Makefile diff --git a/src/image.c b/src/image.c index fb44527..a3bb23e 100644 --- a/src/image.c +++ b/src/image.c @@ -240,29 +240,60 @@ void drawdonut(IMAGECONTENT *ic, const int x, const int y, const float rxp, cons } else { txarc = (int)(360 * (txp / (float)100)); } + } + + // background filled circle + gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 0, 360, ic->cbgoffset, 0); - /* fix possible graphical glitch */ - if (!rxarc) { - rxarc = 1; + if (txarc) { + gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270, 270+txarc, ic->ctxd, gdEdged|gdNoFill); + if (txarc >= 5) { + gdImageFill(ic->im, x+1, y-(DOUTRAD/2-3), ic->ctx); } + gdImageFilledArc(ic->im, x, y, DINRAD, DINRAD, 270, 270+txarc, ic->ctxd, gdEdged|gdNoFill); + } - if (!txarc) { - txarc = 1; + if (rxarc) { + gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270+txarc, 270+txarc+rxarc, ic->crxd, gdEdged|gdNoFill); + if (rxarc >= 5) { + gdImageFill(ic->im, x+(DOUTRAD/2-3)*cos(((270*2+2*txarc+rxarc)/2)*M_PI/180), y+(DOUTRAD/2-3)*sin(((270*2+2*txarc+rxarc)/2)*M_PI/180), ic->crx); } + gdImageFilledArc(ic->im, x, y, DINRAD, DINRAD, 270+txarc, 270+txarc+rxarc, ic->crxd, gdEdged|gdNoFill); } - gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 0, 360, ic->cbgoffset, 0); + // remove center from background filled circle, making it a donut + gdImageFilledArc(ic->im, x, y, DINRAD-2, DINRAD-2, 0, 360, ic->cbackground, 0); +} + +void drawdonut_libgd_native(IMAGECONTENT *ic, const int x, const int y, const float rxp, const float txp) +{ + int rxarc = 0, txarc = 0; if ( (int)(rxp + txp) > 0 ) { + rxarc = (int)(360 * (rxp / (float)100)); + if ( (int)(rxp + txp) == 100 ) { + txarc = 360 - rxarc; + } else { + txarc = (int)(360 * (txp / (float)100)); + } + } + + // background filled circle + gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 0, 360, ic->cbgoffset, 0); + + if (txarc) { gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270, 270+txarc, ic->ctx, 0); gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270, 270+txarc, ic->ctxd, gdEdged|gdNoFill); + gdImageFilledArc(ic->im, x, y, DINRAD, DINRAD, 270, 270+txarc, ic->ctxd, gdEdged|gdNoFill); + } + + if (rxarc) { gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270+txarc, 270+txarc+rxarc, ic->crx, 0); gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270+txarc, 270+txarc+rxarc, ic->crxd, gdEdged|gdNoFill); - - gdImageFilledArc(ic->im, x, y, DINRAD, DINRAD, 270, 270+txarc, ic->ctxd, gdEdged|gdNoFill); gdImageFilledArc(ic->im, x, y, DINRAD, DINRAD, 270+txarc, 270+txarc+rxarc, ic->crxd, gdEdged|gdNoFill); } + // remove center from background filled circle, making it a donut gdImageFilledArc(ic->im, x, y, DINRAD-2, DINRAD-2, 0, 360, ic->cbackground, 0); } diff --git a/src/image.h b/src/image.h index cfd92ba..bb7d130 100644 --- a/src/image.h +++ b/src/image.h @@ -39,6 +39,7 @@ void drawlegend(IMAGECONTENT *ic, const int x, const int y); void drawbar(IMAGECONTENT *ic, const int x, const int y, const int len, const uint64_t rx, const uint64_t tx, const uint64_t max); void drawpole(IMAGECONTENT *ic, const int x, const int y, const int len, const uint64_t rx, const uint64_t tx, const uint64_t max); void drawdonut(IMAGECONTENT *ic, const int x, const int y, const float rxp, const float txp); +void drawdonut_libgd_native(IMAGECONTENT *ic, const int x, const int y, const float rxp, const float txp); void drawhours(IMAGECONTENT *ic, int x, int y, int rate); void drawhourly(IMAGECONTENT *ic, int rate); void drawlist(IMAGECONTENT *ic, const char *listname); diff --git a/tests/dbsql_tests.c b/tests/dbsql_tests.c index 46b074d..b177e8e 100644 --- a/tests/dbsql_tests.c +++ b/tests/dbsql_tests.c @@ -963,21 +963,6 @@ START_TEST(db_validate_with_high_version) } END_TEST -uint64_t get_timestamp(const int year, const int month, const int day, const int hour, const int minute) -{ - struct tm stm; - - memset(&stm, 0, sizeof(struct tm)); - stm.tm_year = year - 1900; - stm.tm_mon = month - 1; - stm.tm_mday = day; - stm.tm_hour = hour; - stm.tm_min = minute; - stm.tm_isdst = -1; - - return (uint64_t)mktime(&stm); -} - void range_test_month_setup(void) { int ret, i; diff --git a/tests/dbsql_tests.h b/tests/dbsql_tests.h index 121e8d8..6dcc97b 100644 --- a/tests/dbsql_tests.h +++ b/tests/dbsql_tests.h @@ -1,7 +1,6 @@ #ifndef DBSQL_TESTS_H #define DBSQL_TESTS_H -uint64_t get_timestamp(const int year, const int month, const int day, const int hour, const int minute); void range_test_month_setup(void); void range_test_hour_setup(void); void add_dbsql_tests(Suite *s); diff --git a/tests/image_tests.c b/tests/image_tests.c index f4b3867..e54a190 100644 --- a/tests/image_tests.c +++ b/tests/image_tests.c @@ -400,6 +400,206 @@ START_TEST(hourly_imagescaling_rate_1000) } END_TEST +START_TEST(output_check) +{ + int ret, x, y; + IMAGECONTENT ic; + FILE *pngout; + + x = 1060; + y = 420; + + defaultcfg(); + initimagecontent(&ic); + ic.im = gdImageCreate(x, y); + colorinit(&ic); + ic.interface.updated = get_timestamp(2001, 2, 3, 4, 5); + layoutinit(&ic, "vnstati output reference test", x, y); + + pngout = fopen("vnstati_check.png", "w"); + ck_assert_ptr_ne(pngout, NULL); + + drawlegend(&ic, 40, 30); + + /* line 1 */ + x = 40; + y = 80; + gdImageStringUp(ic.im, gdFontGetSmall(), 1, y+105, (unsigned char*)"libgd bug workaround", ic.ctext); + drawdonut(&ic, x, y, 0, 0); + drawdonut(&ic, x+55, y, 50.0, 50.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"0/0 - 50/50", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 100.0, 0); + drawdonut(&ic, x+55, y, 0, 100.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"100/0 - 0/100", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 60.0, 40.0); + drawdonut(&ic, x+55, y, 40.0, 60.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"60/40 - 40/60", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 75.0, 25.0); + drawdonut(&ic, x+55, y, 25.0, 75.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"75/25 - 25/75", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 90.0, 10.0); + drawdonut(&ic, x+55, y, 10.0, 90.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"90/10 - 10/90", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 95.0, 5.0); + drawdonut(&ic, x+55, y, 5.0, 95.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"95/5 - 5/95", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 99.0, 1.0); + drawdonut(&ic, x+55, y, 1.0, 99.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"99/1 - 1/99", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 99.9, 0.1); + drawdonut(&ic, x+55, y, 0.1, 99.9); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"99.9/0.1 - 0.1/99.9", ic.ctext); + + /* line 2 */ + x = 40; + y = 160; + drawdonut(&ic, x, y, 0, 0); + drawdonut(&ic, x+55, y, 25.0, 25.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"0/0 - 25/25", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 50.0, 0); + drawdonut(&ic, x+55, y, 0, 50.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"50/0 - 0/50", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 40.0, 30.0); + drawdonut(&ic, x+55, y, 30.0, 40.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"40/30 - 30/40", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 30.0, 20.0); + drawdonut(&ic, x+55, y, 20.0, 30.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"30/20 - 20/30", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 20.0, 10.0); + drawdonut(&ic, x+55, y, 10.0, 20.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"20/10 - 10/20", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 15.0, 5.0); + drawdonut(&ic, x+55, y, 5.0, 15.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"15/5 - 5/15", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 10.0, 1.0); + drawdonut(&ic, x+55, y, 1.0, 10.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"10/1 - 1/10", ic.ctext); + + x += 130; + drawdonut(&ic, x, y, 1.0, 0.1); + drawdonut(&ic, x+55, y, 0.1, 1.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"1.0/0.1 - 0.1/1.0", ic.ctext); + + /* line 3 */ + x = 40; + y = 270; + gdImageStringUp(ic.im, gdFontGetSmall(), 1, y+105, (unsigned char*)"libgd native", ic.ctext); + drawdonut_libgd_native(&ic, x, y, 0, 0); + drawdonut_libgd_native(&ic, x+55, y, 50.0, 50.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"0/0 - 50/50", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 100.0, 0); + drawdonut_libgd_native(&ic, x+55, y, 0, 100.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"100/0 - 0/100", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 60.0, 40.0); + drawdonut_libgd_native(&ic, x+55, y, 40.0, 60.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"60/40 - 40/60", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 75.0, 25.0); + drawdonut_libgd_native(&ic, x+55, y, 25.0, 75.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"75/25 - 25/75", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 90.0, 10.0); + drawdonut_libgd_native(&ic, x+55, y, 10.0, 90.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"90/10 - 10/90", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 95.0, 5.0); + drawdonut_libgd_native(&ic, x+55, y, 5.0, 95.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"95/5 - 5/95", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 99.0, 1.0); + drawdonut_libgd_native(&ic, x+55, y, 1.0, 99.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"99/1 - 1/99", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 99.9, 0.1); + drawdonut_libgd_native(&ic, x+55, y, 0.1, 99.9); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"99.9/0.1 - 0.1/99.9", ic.ctext); + + /* line 4 */ + x = 40; + y = 350; + drawdonut_libgd_native(&ic, x, y, 0, 0); + drawdonut_libgd_native(&ic, x+55, y, 25.0, 25.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"0/0 - 25/25", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 50.0, 0); + drawdonut_libgd_native(&ic, x+55, y, 0, 50.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"50/0 - 0/50", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 40.0, 30.0); + drawdonut_libgd_native(&ic, x+55, y, 30.0, 40.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"40/30 - 30/40", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 30.0, 20.0); + drawdonut_libgd_native(&ic, x+55, y, 20.0, 30.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"30/20 - 20/30", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 20.0, 10.0); + drawdonut_libgd_native(&ic, x+55, y, 10.0, 20.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"20/10 - 10/20", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 15.0, 5.0); + drawdonut_libgd_native(&ic, x+55, y, 5.0, 15.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"15/5 - 5/15", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 10.0, 1.0); + drawdonut_libgd_native(&ic, x+55, y, 1.0, 10.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"10/1 - 1/10", ic.ctext); + + x += 130; + drawdonut_libgd_native(&ic, x, y, 1.0, 0.1); + drawdonut_libgd_native(&ic, x+55, y, 0.1, 1.0); + gdImageString(ic.im, gdFontGetSmall(), x-20, y+30, (unsigned char*)"1.0/0.1 - 0.1/1.0", ic.ctext); + + + + gdImagePng(ic.im, pngout); + ret = fclose(pngout); + ck_assert_int_eq(ret, 0); + gdImageDestroy(ic.im); +} +END_TEST + void add_image_tests(Suite *s) { TCase *tc_image = tcase_create("Image"); @@ -419,5 +619,6 @@ void add_image_tests(Suite *s) tcase_add_test(tc_image, hourly_imagescaling_normal); tcase_add_test(tc_image, hourly_imagescaling_rate_1024); tcase_add_test(tc_image, hourly_imagescaling_rate_1000); + tcase_add_test(tc_image, output_check); suite_add_tcase(s, tc_image); } diff --git a/tests/vnstat_tests.c b/tests/vnstat_tests.c index e68d64d..866059d 100644 --- a/tests/vnstat_tests.c +++ b/tests/vnstat_tests.c @@ -303,3 +303,18 @@ int fake_sys_class_net(const char *iface, const int rx, const int tx, const int return 1; } + +uint64_t get_timestamp(const int year, const int month, const int day, const int hour, const int minute) +{ + struct tm stm; + + memset(&stm, 0, sizeof(struct tm)); + stm.tm_year = year - 1900; + stm.tm_mon = month - 1; + stm.tm_mday = day; + stm.tm_hour = hour; + stm.tm_min = minute; + stm.tm_isdst = -1; + + return (uint64_t)mktime(&stm); +} diff --git a/tests/vnstat_tests.h b/tests/vnstat_tests.h index e642b88..7e83d4b 100644 --- a/tests/vnstat_tests.h +++ b/tests/vnstat_tests.h @@ -15,6 +15,7 @@ int create_zerosize_dbfile(const char *iface); int check_dbfile_exists(const char *iface, const int minsize); int fake_proc_net_dev(const char *mode, const char *iface, const int rx, const int tx, const int rxp, const int txp); int fake_sys_class_net(const char *iface, const int rx, const int tx, const int rxp, const int txp, const int speed); +uint64_t get_timestamp(const int year, const int month, const int day, const int hour, const int minute); #ifndef TESTDIR #define TESTDIR "testdir" -- 2.40.0