/* SPDX-License-Identifier: GPL-2.0-or-later * Exercise the real renderer without any daemon connection or input thread. */ #define main g15stats_application_main #include "../g15stats.c" #undef main #include /* Independent font-bitmap oracle: do not repeat renderString on a second * canvas, since that also repeats any clipping/erasure in the renderer. * The default fonts have zero gap and one blank trailing pixel column. * Compare every cell, including blank pixels, against the font file's bits. */ static void assert_text(g15canvas *canvas, const char *text, int size, int x, int y, int right) { g15font *font = g15r_requestG15DefaultFont(size); assert(font && font->default_gap == 0); assert(font->font_height == font->ascender_height); for (; *text; text++) { unsigned char c = (unsigned char)*text; int width = font->glyph[c].width; int stride = (width + 7) / 8; assert(font->active[c] && font->glyph[c].buffer); assert(x >= 0 && x + width <= 160); assert(y >= 0 && y + font->font_height <= 43); for (int dy = 0; dy < font->font_height; dy++) for (int dx = 0; dx < width; dx++) { int bit = !!(font->glyph[c].buffer[dy * stride + dx / 8] & (0x80 >> (dx % 8))); /* A 13-character first-column header's final blank advance * pixel shares x=105 with the next divider; no ink may do so. */ if (x + dx >= right) { assert(!text[1] && dx == width - 1 && !bit); continue; } if (g15r_getPixel(canvas, x + dx, y + dy) != bit) { fprintf(stderr, "Glyph %c pixel (%d,%d): expected %d, got %d\n", c, x + dx, y + dy, bit, g15r_getPixel(canvas, x + dx, y + dy)); assert(0); } } x += width; } } int main(int argc, char **argv) { g15canvas canvas, before; char tmp[MAX_LINES]; int x, y, variant; size_t i; g15font *font = g15r_requestG15DefaultFont(G15_TEXT_SMALL); assert(font); assert(g15r_testG15FontWidth(font, "T 8192.0PiB") <= 52); assert(g15r_testG15FontWidth(font, "UTL 100%") <= 52); { char lines[5][32]; compact_ram_lines(lines, LLONG_MAX, LLONG_MAX); assert(strstr(lines[1], "PiB") && strstr(lines[2], "PiB")); assert(g15r_testG15FontWidth(font, lines[1]) <= 50); assert(g15r_testG15FontWidth(font, lines[2]) <= 50); compact_ram_lines(lines, 19 * 1048576, 17179869184LL); assert(!strcmp(lines[1], "R 19.0MiB")); assert(!strcmp(lines[2], "T 16.0GiB")); } memset(&canvas, 0xa5, sizeof(canvas)); init_canvas(&canvas); assert(!canvas.mode_xor && !canvas.mode_reverse && !canvas.mode_cache); for (y = 0; y < 43; y++) for (x = 0; x < 160; x++) assert(!g15r_getPixel(&canvas, x, y)); /* Original full-width geometry must remain exact with the column off. */ init_canvas(&before); summary_gpu = 0; drawBar_both(&canvas, 1, 8, 37, 100, 63, 100); g15r_drawBar(&before, BAR_START, 1, BAR_END, 8, G15_COLOR_BLACK, 37, 100, 4); drawBar_reversed(&before, BAR_START, 1, BAR_END, 8, G15_COLOR_BLACK, 63, 100, 5); assert(!memcmp(canvas.buffer, before.buffer, sizeof(canvas.buffer))); glibtop_init(); /* No device fixtures: sample the actual selected GPU when requested. */ assert(!gpu_detect(argc > 1 ? argv[1] : "off")); if (gpu_count && gpu_devices[0].backend) { long long start = gpu_milliseconds(); assert(!gpu_start()); do { gpu_frame frame = gpu_snapshot(); for (i = 0; i < frame.count; i++) if (frame.samples[i].total < 0) break; if (i == frame.count) break; poll(NULL, 0, 10); } while (gpu_milliseconds() - start < 1800); assert(i == gpu_count); assert(!gpu_stop()); } /* No-argument make check retains the worst-width fixture. Explicit off * must exercise the application's actual zero-GPU/full-width layout. * Optional count/page/profile arguments are in-memory visual cases only. */ if (!gpu_count && (argc == 1 || argc > 3)) { gpu_count = argc > 3 ? (size_t)atoi(argv[3]) : 2; assert(gpu_count >= 1 && gpu_count <= GPU_MAX); gpu_devices[0].backend = 2; gpu_devices[1].backend = 1; strcpy(gpu_devices[0].bus, "0000:01:00.0"); strcpy(gpu_devices[1].bus, "0000:11:00.0"); gpu_publish(0, (gpu_sample){100, LLONG_MAX, LLONG_MAX, -273}); gpu_publish(1, (gpu_sample){100, LLONG_MAX, LLONG_MAX, 1000}); for (i = 2; i < gpu_count; i++) { gpu_devices[i].backend = 1; snprintf(gpu_devices[i].bus, 32, "0001:%02x:1f.7", (unsigned)i); gpu_publish(i, i % 2 ? gpu_unknown : (gpu_sample){100, LLONG_MAX, LLONG_MAX, -273}); } if (gpu_count > 2) { /* Exercise every pair at the maximum inventory, including the * 13-character AMD64 header and an odd final card. */ for (i = 0; i < (gpu_count + 1) / 2; i++) { gpu_page = i; gpu_page_due = LLONG_MAX; g15r_clearScreen(&canvas, G15_COLOR_WHITE); draw_gpu_column(&canvas); for (y = 35; y < 43; y++) for (x = 0; x < 160; x++) assert(!g15r_getPixel(&canvas, x, y)); } /* Keep the final pair for the exported preview and pixel checks. */ } } if (argc > 4) { gpu_page = (size_t)atoi(argv[4]); assert(gpu_page < (gpu_count + 1) / 2); gpu_page_due = LLONG_MAX; } if (argc > 5) { assert(!strcmp(argv[5], "large") || !strcmp(argv[5], "unknown")); for (i = 0; i < gpu_count; i++) gpu_publish(i, !strcmp(argv[5], "unknown") ? gpu_unknown : (gpu_sample){100, 100LL << 30, 192LL << 30, 1000}); } for (i = 0; i < gpu_count; i++) { char lines[5][32]; int row; gpu_lines(i, gpu_cache[i], lines); for (row = 0; row < 5; row++) { assert(g15r_testG15FontWidth(font, lines[row]) <= 52); printf("GPU %zu row %d: %s\n", i + 1, row, lines[row]); } } for (variant = 0; variant < 2; variant++) { cycle = SCREEN_SUMMARY; mode[SCREEN_SUMMARY] = variant; have_temp = have_fan = 0; /* Keep legacy generic hwmon probing out of QA. */ summary_gpu = gpu_count != 0; draw_cpu_screen_multicore(&canvas, tmp, 0); before = canvas; draw_gpu_column(&canvas); for (y = 0; y < 43; y++) for (x = 0; x < 160; x++) if (x < (gpu_count > 1 ? 51 : 105) || y >= 35) assert(g15r_getPixel(&canvas, x, y) == g15r_getPixel(&before, x, y)); /* Re-render each column on an empty canvas: exact pixels prove neither * neighboring column nor CPU text overlaps it, including edge glyphs. */ { gpu_frame frame = gpu_snapshot(); size_t first = gpu_page_first(frame.count, gpu_page * 5000ULL); for (i = first; i < frame.count && i < first + 2; i++) { g15canvas isolated; char lines[5][32]; int row, left = frame.count == 1 ? 108 : 54 + (int)(i-first) * 54; init_canvas(&isolated); gpu_lines(i, frame.samples[i], lines); for (row = 0; row < 5; row++) g15r_renderString(&isolated, (unsigned char *)lines[row], 0, G15_TEXT_SMALL, left, 1+row*7); for (y = 0; y < 35; y++) for (x = left; x < (left == 54 ? 105 : 160); x++) assert(g15r_getPixel(&canvas, x, y) == g15r_getPixel(&isolated, x, y)); } } summary_gpu = 0; print_time_info(&canvas, tmp); assert(g15r_testG15FontWidth(font, tmp) <= 160); assert_text(&canvas, tmp, G15_TEXT_SMALL, 80 - (int)strlen(tmp) * 4 / 2, INFO_ROW, 160); if (gpu_count < 2) { const char *label = "TOTAL"; for (int row = 0; row < 5; row++) { char letter[2] = {label[row], 0}; assert_text(&canvas, letter, G15_TEXT_MED, gpu_count ? 98 : TEXT_RIGHT, row * 7, 160); } } { gpu_frame frame = gpu_snapshot(); size_t first = gpu_page_first(frame.count, gpu_page * 5000ULL); for (i = first; i < frame.count && i < first + 2; i++) { char lines[5][32]; int left = frame.count == 1 ? 108 : 54 + (int)(i-first) * 54; gpu_lines(i, frame.samples[i], lines); for (int row = 0; row < 5; row++) assert_text(&canvas, lines[row], G15_TEXT_SMALL, left, 1+row*7, left == 54 ? 105 : 160); } } if (argc > 2) { char path[PATH_MAX]; snprintf(path, sizeof(path), "%s-%d.bin", argv[2], variant); FILE *raw = fopen(path, "wb"); assert(raw); assert(fwrite(canvas.buffer, 1, 160 * 43 / 8, raw) == 160 * 43 / 8); assert(!fclose(raw)); snprintf(path, sizeof(path), "%s-%d.pgm", argv[2], variant); FILE *out = fopen(path, "w"); assert(out); fprintf(out, "P2\n160 43\n255\n"); for (y = 0; y < 43; y++) { for (x = 0; x < 160; x++) fprintf(out, "%d ", g15r_getPixel(&canvas, x, y) ? 0 : 255); fputc('\n', out); } assert(!fclose(out)); } } if (argc > 2) { FILE *out = fopen(argv[2], "w"); assert(out); fprintf(out, "P2\n160 43\n255\n"); for (y = 0; y < 43; y++) { for (x = 0; x < 160; x++) fprintf(out, "%d ", g15r_getPixel(&canvas, x, y) ? 0 : 255); fputc('\n', out); } assert(!fclose(out)); } glibtop_close(); puts("PASS real LCD buffer: initialization, original geometry, both summary variants, independent glyph bits, complete TOTAL/footer, column bounds"); return 0; }