#include #include #include #include #include /* Type identifier for fbar */ static int fbar_type_id = -1; fbar *fbar_create(void *parent) { if(fbar_type_id < 0) return NULL; fbar *b = malloc(sizeof *b); if(!b) return NULL; jwidget_init(&b->widget, fbar_type_id, parent); b->tests = NULL; b->lists = NULL; jwidget_set_margin(b, _(1,0), _(4,8), _(0,2), _(4,8)); return b; } void fbar_set_tests(fbar *b, ft_test **tests) { b->tests = tests; b->lists = NULL; b->widget.update = 1; } void fbar_set_lists(fbar *b, ft_list *lists) { b->tests = NULL; b->lists = lists; b->widget.update = 1; } //--- // Counting statistics //--- struct stats { /* Number of tests done, pending and empty (proportion of full bar) */ int16_t done; int16_t pending; int16_t empty; /* Number of assertions (proportion of the done section of the bar) */ int16_t passed; int16_t skipped; int16_t failed; }; static void stats_init(struct stats *st) { memset(st, 0, sizeof *st); } static void stats_add_test(struct stats *st, ft_test *t) { st->passed += t->passed; st->skipped += t->skipped; st->failed += t->failed; if(t->function && !t->run) st->pending++; else if(t->passed + t->skipped + t->failed) st->done++; else st->empty++; } static void stats_add_list(struct stats *st, ft_list *l) { if(!l->children) return; for(int i = 0; l->children[i]; i++) stats_add_test(st, l->children[i]); } //--- // Polymorphic widget operations //--- static void fbar_poly_csize(void *b0) { fbar *b = b0; b->widget.w = _(75,120); b->widget.h = _(5,13); } #ifdef FX9860G static int block(int rx, int y, GUNUSED int h, int px, int val, int fg) { if(!px) return 0; extern bopti_image_t img_status; dsubimage(rx, y, &img_status, 6*(fg-1), 0, 5, 5, DIMAGE_NONE); font_t const *old_font = dfont(&font_mini); int w; char str[16]; sprintf(str, "%d", val); dsize(str, NULL, &w, NULL); dtext(rx+6, y, C_BLACK, str); dfont(old_font); return w + 11; } #else static int block(int rx, int y, int h, int px, int val, int fg) { drect(rx, y, rx+px-1, y+h-1, fg); dprint_opt(rx+px/2, y+2, C_BLACK, C_NONE, DTEXT_CENTER, DTEXT_TOP, "%d", val); return px; } #endif static void fbar_poly_render(void *b0, int x, int y) { fbar *b = b0; int w = jwidget_content_width(b); int h = jwidget_content_height(b); struct stats st, px; stats_init(&st); stats_init(&px); if(b->tests) for(int i = 0; b->tests[i]; i++) { stats_add_test(&st, b->tests[i]); } else if(b->lists) for(int i = 0; b->lists[i].name; i++) { stats_add_list(&st, &b->lists[i]); } /* Width of bar for each of the 3 main sections */ int main = st.done + st.pending + st.empty; if(main == 0) { drect_border(x, y, x+w-1, y+h-1, C_WHITE, 1, C_BLACK); return; } /* Try go guarantee at least 10 pixels per section */ int guaranteeable_min = 10 * (!!st.done + !!st.pending + !!st.empty); bool guaranteed = (w > guaranteeable_min); if(guaranteed) w -= guaranteeable_min; px.done = (st.done * w) / main; px.pending = (st.pending * w) / main; px.empty = (st.empty * w) / main; int rest = w - px.done - px.pending - px.empty; if(rest > 0) { if(st.done) px.done += rest; else if(st.pending) px.pending += rest; else if(st.empty) px.empty += rest; } if(guaranteed) { if(st.done) px.done += 10; if(st.pending) px.pending += 10; if(st.empty) px.empty += 10; w += guaranteeable_min; } /* Width of bar for each of the subsections of done */ int assertions = st.passed + st.skipped + st.failed; if(assertions == 0) assertions = 1; /* Try to guarantee each section the size of its text, +2 pixels */ int min_passed=0, min_skipped=0, min_failed=0; char str[10]; if(st.passed) { sprintf(str, "%d", st.passed); dsize(str, NULL, &min_passed, NULL); min_passed += 2; } if(st.skipped) { sprintf(str, "%d", st.skipped); dsize(str, NULL, &min_skipped, NULL); min_skipped += 2; } if(st.failed) { sprintf(str, "%d", st.failed); dsize(str, NULL, &min_failed, NULL); min_failed += 2; } guaranteeable_min = min_passed + min_skipped + min_failed; guaranteed = (px.done > guaranteeable_min); if(guaranteed) px.done -= guaranteeable_min; px.skipped = (st.skipped * px.done) / assertions; px.failed = (st.failed * px.done) / assertions; px.passed = px.done - px.skipped - px.failed; if(guaranteed) { px.passed += min_passed; px.skipped += min_skipped; px.failed += min_failed; px.done += guaranteeable_min; } /* Draw */ int rx = x; rx += block(rx, y, h, px.passed, st.passed, FT_TEST_PASSED); rx += block(rx, y, h, px.skipped, st.skipped, FT_TEST_SKIPPED); rx += block(rx, y, h, px.failed, st.failed, FT_TEST_FAILED); rx += block(rx, y, h, px.pending, st.pending, FT_TEST_PENDING); rx += block(rx, y, h, px.empty, st.empty, FT_TEST_EMPTY); #ifdef FXCG50 /* Darken the border for a cool effect */ for(int dx = 0; dx < w; dx++) { int i1 = 396 * (y) + (x + dx); int i2 = 396 * (y + h - 1) + (x + dx); gint_vram[i1] = (gint_vram[i1] & 0xf7de) >> 1; gint_vram[i2] = (gint_vram[i2] & 0xf7de) >> 1; } for(int dy = 1; dy < h-1; dy++) { int i1 = 396 * (y + dy) + (x); int i2 = 396 * (y + dy) + (x + w - 1); gint_vram[i1] = (gint_vram[i1] & 0xf7de) >> 1; gint_vram[i2] = (gint_vram[i2] & 0xf7de) >> 1; } #endif } /* fbar type definition */ static jwidget_poly type_fbar = { .name = "fbar", .csize = fbar_poly_csize, .render = fbar_poly_render, }; /* Type registration */ __attribute__((constructor(2000))) static void j_register_fbar(void) { fbar_type_id = j_register_widget(&type_fbar, "jwidget"); }