test.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #include <stdbool.h>
  5. #include <string.h>
  6. #include <errno.h>
  7. #include "./assets/tsodinPog.c"
  8. #include "./assets/tsodinCup.c"
  9. #define PI 3.14159265359
  10. #define return_defer(value) do { result = (value); goto defer; } while (0)
  11. #define UNUSED(x) (void)(x)
  12. #define UNIMPLEMENTED(message) \
  13. do { \
  14. fprintf(stderr, "%s:%d: UNIMPLEMENTED: %s\n", __FILE__, __LINE__, message); \
  15. exit(1); \
  16. } while (0)
  17. #define UNREACHABLE(message) \
  18. do { \
  19. fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); \
  20. exit(1); \
  21. } while (0)
  22. #define ARENA_IMPLEMENTATION
  23. #include "./arena.h"
  24. static Arena default_arena = {0};
  25. static Arena *context_arena = &default_arena;
  26. static void *context_alloc(size_t size)
  27. {
  28. assert(context_arena);
  29. return arena_alloc(context_arena, size);
  30. }
  31. static void *context_realloc(void *oldp, size_t oldsz, size_t newsz)
  32. {
  33. if (newsz <= oldsz) return oldp;
  34. return memcpy(context_alloc(newsz), oldp, oldsz);
  35. }
  36. #define STBI_MALLOC context_alloc
  37. #define STBI_FREE UNUSED
  38. #define STBI_REALLOC_SIZED context_realloc
  39. #define STB_IMAGE_IMPLEMENTATION
  40. #include "./stb_image.h"
  41. #define STBIW_MALLOC STBI_MALLOC
  42. #define STBIW_FREE STBI_FREE
  43. #define STBIW_REALLOC_SIZED STBI_REALLOC_SIZED
  44. #define STB_IMAGE_WRITE_IMPLEMENTATION
  45. #include "./stb_image_write.h"
  46. #define OLIVEC_IMPLEMENTATION
  47. #include "olive.c"
  48. #define BACKGROUND_COLOR 0xFF202020
  49. #define FOREGROUND_COLOR 0xFF2020FF
  50. #define WHITE_COLOR 0xFFAAAAAA
  51. #define RED_COLOR 0xFF2020AA
  52. #define GREEN_COLOR 0xFF20AA20
  53. #define BLUE_COLOR 0xFFAA2020
  54. #define ERROR_COLOR 0xFFFF00FF
  55. #define TEST_DIR_PATH "./test"
  56. bool canvas_stbi_load(const char *file_path, Olivec_Canvas *oc)
  57. {
  58. int width, height;
  59. uint32_t *pixels = (uint32_t*) stbi_load(file_path, &width, &height, NULL, 4);
  60. if (pixels == NULL) return false;
  61. *oc = olivec_canvas(pixels, width, height, width);
  62. return true;
  63. }
  64. bool canvas_stbi_save(Olivec_Canvas oc, const char *file_path)
  65. {
  66. return stbi_write_png(file_path, oc.width, oc.height, 4, oc.pixels, sizeof(uint32_t)*oc.stride);
  67. }
  68. typedef struct {
  69. Olivec_Canvas (*generate_actual_canvas)(void);
  70. const char *id;
  71. const char *expected_file_path;
  72. const char *actual_file_path;
  73. const char *diff_file_path;
  74. } Test_Case;
  75. #define DEFINE_TEST_CASE(name) \
  76. { \
  77. .generate_actual_canvas = test_##name, \
  78. .id = #name, \
  79. .expected_file_path = TEST_DIR_PATH "/" #name "_expected.png", \
  80. .actual_file_path = TEST_DIR_PATH "/" #name "_actual.png", \
  81. .diff_file_path = TEST_DIR_PATH "/" #name "_diff.png", \
  82. }
  83. bool update_test_case(const Test_Case *tc)
  84. {
  85. Olivec_Canvas actual_canvas = tc->generate_actual_canvas();
  86. const char *expected_file_path = tc->expected_file_path;
  87. if (!canvas_stbi_save(actual_canvas, expected_file_path)) {
  88. fprintf(stderr, "ERROR: could not write file %s: %s\n", expected_file_path, strerror(errno));
  89. return(false);
  90. }
  91. printf("%s: Generated %s\n", tc->id, expected_file_path);
  92. return(true);
  93. }
  94. Olivec_Canvas canvas_alloc(size_t width, size_t height)
  95. {
  96. uint32_t *pixels = context_alloc(sizeof(uint32_t)*width*height);
  97. return olivec_canvas(pixels, width, height, width);
  98. }
  99. typedef enum {
  100. REPLAY_PASSED,
  101. REPLAY_FAILED,
  102. REPLAY_ERRORED,
  103. } Replay_Result;
  104. static inline size_t min_size(size_t a, size_t b)
  105. {
  106. if (a < b) return a;
  107. return b;
  108. }
  109. static inline size_t max_size(size_t a, size_t b)
  110. {
  111. if (a > b) return a;
  112. return b;
  113. }
  114. Replay_Result run_test_case(const char *program_path, const Test_Case *tc)
  115. {
  116. printf("%s:", tc->id);
  117. fflush(stdout);
  118. const char *expected_file_path = tc->expected_file_path;
  119. const char *actual_file_path = tc->actual_file_path;
  120. const char *diff_file_path = tc->diff_file_path;
  121. Olivec_Canvas actual_canvas = tc->generate_actual_canvas();
  122. Olivec_Canvas expected_canvas;
  123. if (!canvas_stbi_load(expected_file_path, &expected_canvas)) {
  124. fprintf(stderr, "\n");
  125. fprintf(stderr, " ERROR: could not read %s: %s\n", expected_file_path, stbi_failure_reason());
  126. if (errno == ENOENT) {
  127. fprintf(stderr, " HINT: Consider running `$ %s update %s` to create it\n", program_path, tc->id);
  128. }
  129. return(REPLAY_ERRORED);
  130. }
  131. bool failed = false;
  132. if (expected_canvas.width != actual_canvas.width || expected_canvas.height != actual_canvas.height) {
  133. failed = true;
  134. }
  135. Olivec_Canvas diff_canvas =
  136. canvas_alloc(
  137. max_size(expected_canvas.width, actual_canvas.width),
  138. max_size(expected_canvas.height, actual_canvas.height));
  139. olivec_fill(diff_canvas, ERROR_COLOR);
  140. for (size_t y = 0; y < min_size(expected_canvas.height, actual_canvas.height); ++y) {
  141. for (size_t x = 0; x < min_size(expected_canvas.width, actual_canvas.width); ++x) {
  142. uint32_t expected_pixel = OLIVEC_PIXEL(expected_canvas, x, y);
  143. uint32_t actual_pixel = OLIVEC_PIXEL(actual_canvas, x, y);
  144. if (expected_pixel != actual_pixel) {
  145. OLIVEC_PIXEL(diff_canvas, x, y) = ERROR_COLOR;
  146. failed = true;
  147. } else {
  148. OLIVEC_PIXEL(diff_canvas, x, y) = expected_pixel;
  149. }
  150. }
  151. }
  152. if (failed) {
  153. fprintf(stderr, "\n");
  154. if (!canvas_stbi_save(actual_canvas, actual_file_path)) {
  155. fprintf(stderr, " ERROR: could not write image file with actual pixels %s: %s\n", actual_file_path, strerror(errno));
  156. return(REPLAY_ERRORED);
  157. }
  158. if (!canvas_stbi_save(diff_canvas, diff_file_path)) {
  159. fprintf(stderr, " ERROR: could not wrilte diff image file %s: %s\n", diff_file_path, strerror(errno));
  160. return(REPLAY_ERRORED);
  161. }
  162. fprintf(stderr, " TEST FAILURE: unexpected pixels in generated image\n");
  163. fprintf(stderr, " Expected: %s\n", expected_file_path);
  164. fprintf(stderr, " Actual: %s\n", actual_file_path);
  165. fprintf(stderr, " Diff: %s\n", diff_file_path);
  166. fprintf(stderr, " HINT: If this behaviour is intentional confirm that by updating the image with `$ %s update`\n", program_path);
  167. return(REPLAY_FAILED);
  168. }
  169. printf(" OK\n");
  170. return(REPLAY_PASSED);
  171. }
  172. Olivec_Canvas test_fill_rect(void)
  173. {
  174. size_t width = 128;
  175. size_t height = 128;
  176. Olivec_Canvas oc = canvas_alloc(width, height);
  177. olivec_fill(oc, BACKGROUND_COLOR);
  178. olivec_rect(oc, width/2 - width/8, height/2 - height/8, width/4, height/4, RED_COLOR);
  179. olivec_rect(oc, width - 1, height - 1, -width/2, -height/2, GREEN_COLOR);
  180. olivec_rect(oc, -width/4, -height/4, width/2, height/2, BLUE_COLOR);
  181. return oc;
  182. }
  183. Olivec_Canvas test_fill_circle(void)
  184. {
  185. size_t width = 128;
  186. size_t height = 128;
  187. Olivec_Canvas oc = canvas_alloc(width, height);
  188. olivec_fill(oc, BACKGROUND_COLOR);
  189. olivec_circle(oc, 0, 0, width/2, RED_COLOR);
  190. olivec_circle(oc, width/2, height/2, width/4, BLUE_COLOR);
  191. olivec_circle(oc, width*3/4, height*3/4, -width/4, GREEN_COLOR);
  192. return oc;
  193. }
  194. Olivec_Canvas test_draw_line(void)
  195. {
  196. size_t width = 128;
  197. size_t height = 128;
  198. Olivec_Canvas oc = canvas_alloc(width, height);
  199. olivec_fill(oc, BACKGROUND_COLOR);
  200. olivec_line(oc, 0, 0, width, height, RED_COLOR);
  201. olivec_line(oc, width, 0, 0, height, BLUE_COLOR);
  202. olivec_line(oc, width/2, 0, width/2, height, GREEN_COLOR);
  203. return oc;
  204. }
  205. Olivec_Canvas test_fill_triangle(void)
  206. {
  207. size_t width = 128;
  208. size_t height = 128;
  209. Olivec_Canvas oc = canvas_alloc(width, height);
  210. olivec_fill(oc, BACKGROUND_COLOR);
  211. {
  212. int x1 = width/2, y1 = height/8;
  213. int x2 = width/8, y2 = height/2;
  214. int x3 = width*7/8, y3 = height*7/8;
  215. olivec_triangle(oc, x1, y1, x2, y2, x3, y3, RED_COLOR);
  216. }
  217. {
  218. int x1 = width/2, y1 = height*2/8;
  219. int x2 = width*2/8, y2 = height/2;
  220. int x3 = width*6/8, y3 = height/2;
  221. olivec_triangle(oc, x1, y1, x2, y2, x3, y3, GREEN_COLOR);
  222. }
  223. {
  224. int x1 = width/8, y1 = height/8;
  225. int x2 = width/8, y2 = height*3/8;
  226. int x3 = width*3/8, y3 = height*3/8;
  227. olivec_triangle(oc, x1, y1, x2, y2, x3, y3, BLUE_COLOR);
  228. }
  229. return oc;
  230. }
  231. Olivec_Canvas test_alpha_blending(void)
  232. {
  233. size_t width = 128;
  234. size_t height = 128;
  235. Olivec_Canvas oc = canvas_alloc(width, height);
  236. olivec_fill(oc, BACKGROUND_COLOR);
  237. olivec_rect(oc, 0, 0, width*3/4, height*3/4, RED_COLOR);
  238. olivec_rect(oc, width-1, height-1, -width*3/4, -height*3/4, 0x5520AA20);
  239. olivec_circle(oc, width/2, height/2, width/4, 0xBBAA2020);
  240. olivec_triangle(oc, 0, height-1, width-1, height-1, width/2, 0, 0xBB20AAAA);
  241. olivec_triangle3c(oc, 0, 0, width-1, 0, width/2, height-1, 0xBB2020AA, 0xBB20AA20, 0xBBAA2020);
  242. return oc;
  243. }
  244. Olivec_Canvas test_checker_example(void)
  245. {
  246. int width = 800/2;
  247. int height = 600/2;
  248. int cols = (8*2);
  249. int rows = (6*2);
  250. int cell_width = (width/cols);
  251. int cell_height = (height/rows);
  252. Olivec_Canvas oc = canvas_alloc(width, height);
  253. olivec_fill(oc, BACKGROUND_COLOR);
  254. for (int y = 0; y < rows; ++y) {
  255. for (int x = 0; x < cols; ++x) {
  256. uint32_t color = BACKGROUND_COLOR;
  257. if ((x + y)%2 == 0) {
  258. color = 0xFF2020FF;
  259. }
  260. olivec_rect(oc, x*cell_width, y*cell_height, cell_width, cell_height, color);
  261. }
  262. }
  263. return oc;
  264. }
  265. Olivec_Canvas test_circle_example(void)
  266. {
  267. int width = 800/2;
  268. int height = 600/2;
  269. int cols = (8*2);
  270. int rows = (6*2);
  271. int cell_width = (width/cols);
  272. int cell_height = (height/rows);
  273. Olivec_Canvas oc = canvas_alloc(width, height);
  274. olivec_fill(oc, BACKGROUND_COLOR);
  275. for (int y = 0; y < rows; ++y) {
  276. for (int x = 0; x < cols; ++x) {
  277. float u = (float)x/cols;
  278. float v = (float)y/rows;
  279. float t = (u + v)/2;
  280. int radius = cell_width;
  281. if (cell_height < radius) radius = cell_height;
  282. olivec_circle(oc,
  283. x*cell_width + cell_width/2, y*cell_height + cell_height/2,
  284. (size_t) (radius/8*(1 - t) + radius/2*t),
  285. FOREGROUND_COLOR);
  286. }
  287. }
  288. return oc;
  289. }
  290. Olivec_Canvas test_lines_circle(void)
  291. {
  292. int width = 800/2;
  293. int height = 600/2;
  294. Olivec_Canvas oc = canvas_alloc(width, height);
  295. olivec_fill(oc, BACKGROUND_COLOR);
  296. size_t n = 20;
  297. float angle = 2*PI/n;
  298. float length = width;
  299. if (length > height) length = height;
  300. length /= 3;
  301. float x1 = width/2;
  302. float y1 = height/2;
  303. for (size_t i = 0; i < n; ++i) {
  304. float x2 = x1 + cosf(angle*i)*length;
  305. float y2 = y1 + sinf(angle*i)*length;
  306. olivec_line(oc, x1, y1, x2, y2, 0xFF1818FF);
  307. }
  308. return oc;
  309. }
  310. Olivec_Canvas test_lines_example(void)
  311. {
  312. int width = 800/2;
  313. int height = 600/2;
  314. Olivec_Canvas oc = canvas_alloc(width, height);
  315. olivec_fill(oc, BACKGROUND_COLOR);
  316. olivec_line(oc, 0, 0, width, height, FOREGROUND_COLOR);
  317. olivec_line(oc, width, 0, 0, height, FOREGROUND_COLOR);
  318. olivec_line(oc, 0, 0, width/4, height, 0xFF20FF20);
  319. olivec_line(oc, width/4, 0, 0, height, 0xFF20FF20);
  320. olivec_line(oc, width, 0, width/4*3, height, 0xFF20FF20);
  321. olivec_line(oc, width/4*3, 0, width, height, 0xFF20FF20);
  322. olivec_line(oc, 0, height/2, width, height/2, 0xFFFF3030);
  323. olivec_line(oc, width/2, 0, width/2, height, 0xFFFF3030);
  324. return oc;
  325. }
  326. Olivec_Canvas test_hello_world_text_rendering(void)
  327. {
  328. size_t size = 5;
  329. const char *text = "hello, world";
  330. size_t text_len = strlen(text);
  331. Olivec_Canvas oc = canvas_alloc(400, 150);
  332. olivec_fill(oc, BACKGROUND_COLOR);
  333. olivec_text(oc, text, oc.width/2 - OLIVEC_DEFAULT_FONT_WIDTH*size*text_len/2, oc.height/2 - OLIVEC_DEFAULT_FONT_HEIGHT*size/2, olivec_default_font, size, FOREGROUND_COLOR);
  334. return oc;
  335. }
  336. Olivec_Canvas test_line_edge_cases(void)
  337. {
  338. size_t width = 10;
  339. size_t height = 10;
  340. Olivec_Canvas oc = canvas_alloc(width, height);
  341. olivec_fill(oc, BACKGROUND_COLOR);
  342. // One pixel line
  343. olivec_line(oc, width/2, height/2, width/2, height/2, FOREGROUND_COLOR);
  344. // Out-of-bounds horizontally
  345. olivec_line(oc, width + 10, height/2, width + 20, height/2, FOREGROUND_COLOR);
  346. // Out-of-bounds vertically
  347. olivec_line(oc, width/2, height + 10, width/2, height + 20, FOREGROUND_COLOR);
  348. return oc;
  349. }
  350. Olivec_Canvas test_frame(void)
  351. {
  352. size_t width = 256;
  353. size_t height = 128;
  354. Olivec_Canvas oc = canvas_alloc(width, height);
  355. olivec_fill(oc, BACKGROUND_COLOR);
  356. {
  357. size_t w = width/2;
  358. size_t h = height/2;
  359. olivec_frame(oc, 0, 0, w, h, 1, RED_COLOR);
  360. }
  361. {
  362. olivec_frame(oc, width/2, height/2, width, height, 1, GREEN_COLOR);
  363. }
  364. // Odd thiccness
  365. {
  366. size_t w = width/2;
  367. size_t h = height/2;
  368. size_t t = 5;
  369. olivec_frame(oc, width/2 - w/2, height/2 - h/2, w, h, t, WHITE_COLOR);
  370. olivec_frame(oc, width/2 - w/2, height/2 - h/2, w, h, 1, RED_COLOR);
  371. }
  372. // Even thiccness
  373. {
  374. size_t w = width/4 + 1;
  375. size_t h = height/4;
  376. size_t t = 6;
  377. olivec_frame(oc, width/2 - w/2, height/2 - h/2, w, h, t, WHITE_COLOR);
  378. olivec_frame(oc, width/2 - w/2, height/2 - h/2, w, h, 1, RED_COLOR);
  379. }
  380. // Zero thiccness
  381. {
  382. size_t w = width/8;
  383. size_t h = height/8;
  384. size_t t = 0;
  385. olivec_frame(oc, width/2 - w/2, height/2 - h/2, w, h, t, WHITE_COLOR);
  386. }
  387. return oc;
  388. }
  389. Olivec_Canvas test_sprite_blend(void)
  390. {
  391. size_t width = 128;
  392. size_t height = 128;
  393. Olivec_Canvas dst = canvas_alloc(width, height);
  394. olivec_fill(dst, RED_COLOR);
  395. Olivec_Canvas src = canvas_alloc(width, height);
  396. for (size_t y = 0; y < src.height; ++y) {
  397. for (size_t x = 0; x < src.width; ++x) {
  398. if ((x + y)%2 == 0) {
  399. OLIVEC_PIXEL(src, x, y) = 0;
  400. } else {
  401. OLIVEC_PIXEL(src, x, y) = GREEN_COLOR;
  402. }
  403. }
  404. }
  405. olivec_sprite_blend(dst, 0, 0, width, height, src);
  406. return dst;
  407. }
  408. Olivec_Canvas test_sprite_blend_out_of_bounds_cut(void)
  409. {
  410. size_t width = 128;
  411. size_t height = 128;
  412. Olivec_Canvas dst = canvas_alloc(width, height);
  413. Olivec_Canvas src = olivec_canvas(tsodinPog_pixels, tsodinPog_width, tsodinPog_height, tsodinPog_width);
  414. olivec_fill(dst, RED_COLOR);
  415. olivec_sprite_blend(dst, -width/2, -height/2, width, height, src);
  416. olivec_sprite_blend(dst, width/2, -height/2, width, height, src);
  417. olivec_sprite_blend(dst, -width/2, height/2, width, height, src);
  418. olivec_sprite_blend(dst, width/2, height/2, width, height, src);
  419. return dst;
  420. }
  421. Olivec_Canvas test_sprite_blend_flip(void)
  422. {
  423. size_t width = 128;
  424. size_t height = 128;
  425. Olivec_Canvas dst = canvas_alloc(width, height);
  426. Olivec_Canvas src = olivec_canvas(tsodinPog_pixels, tsodinPog_width, tsodinPog_height, tsodinPog_width);
  427. olivec_fill(dst, RED_COLOR);
  428. olivec_sprite_blend(dst, 0, 0, width/2, height/2, src);
  429. olivec_sprite_blend(dst, width - 1, 0, -width/2, height/2, src);
  430. olivec_sprite_blend(dst, 0, height - 1, width/2, -height/2, src);
  431. olivec_sprite_blend(dst, width - 1, height - 1, -width/2, -height/2, src);
  432. return dst;
  433. }
  434. Olivec_Canvas test_sprite_blend_flip_cut(void)
  435. {
  436. size_t width = 128;
  437. size_t height = 128;
  438. Olivec_Canvas dst = canvas_alloc(width, height);
  439. Olivec_Canvas src = olivec_canvas(tsodinPog_pixels, tsodinPog_width, tsodinPog_height, tsodinPog_width);
  440. olivec_fill(dst, RED_COLOR);
  441. olivec_sprite_blend(dst, -width/2, -height/2, width, height, src);
  442. olivec_sprite_blend(dst, width - 1 + width/2, -height/2, -width, height, src);
  443. olivec_sprite_blend(dst, -width/2, height - 1 + height/2, width, -height, src);
  444. olivec_sprite_blend(dst, width - 1 + width/2, height - 1 + height/2, -width, -height, src);
  445. return dst;
  446. }
  447. Olivec_Canvas test_empty_rect(void)
  448. {
  449. size_t w = 8;
  450. size_t h = 8;
  451. Olivec_Canvas dst = canvas_alloc(w, h);
  452. olivec_fill(dst, BACKGROUND_COLOR);
  453. olivec_rect(dst, w/2, h/2, 0, 0, FOREGROUND_COLOR);
  454. return dst;
  455. }
  456. Olivec_Canvas test_sprite_blend_empty_rect(void)
  457. {
  458. size_t w = 8;
  459. size_t h = 8;
  460. Olivec_Canvas dst = canvas_alloc(w, h);
  461. Olivec_Canvas src = olivec_canvas(tsodinPog_pixels, tsodinPog_width, tsodinPog_height, tsodinPog_width);
  462. olivec_fill(dst, BACKGROUND_COLOR);
  463. olivec_sprite_blend(dst, 0, 0, 0, 0, src);
  464. return dst;
  465. }
  466. Olivec_Canvas test_sprite_blend_null(void)
  467. {
  468. size_t w = 8;
  469. size_t h = 8;
  470. Olivec_Canvas dst = canvas_alloc(w, h);
  471. olivec_fill(dst, BACKGROUND_COLOR);
  472. olivec_sprite_blend(dst, 0, 0, w, h, OLIVEC_CANVAS_NULL);
  473. return dst;
  474. }
  475. Olivec_Canvas test_sprite_blend_vs_copy(void)
  476. {
  477. Olivec_Canvas tsodinCup = olivec_canvas(tsodinCup_pixels, tsodinCup_width, tsodinCup_height, tsodinCup_width);
  478. size_t w = tsodinCup.width;
  479. size_t h = tsodinCup.height*2;
  480. Olivec_Canvas dst = canvas_alloc(w, h);
  481. olivec_fill(dst, RED_COLOR);
  482. olivec_sprite_blend(dst, 0, 0, tsodinCup.width, tsodinCup.height, tsodinCup);
  483. olivec_sprite_copy(dst, 0, tsodinCup.height, tsodinCup.width, tsodinCup.height, tsodinCup);
  484. return dst;
  485. }
  486. Olivec_Canvas test_barycentric_overflow(void)
  487. {
  488. size_t w = 256;
  489. size_t h = 256;
  490. Olivec_Canvas dst = canvas_alloc(w, h);
  491. olivec_fill(dst, 0xFF181818);
  492. olivec_triangle3c(dst, w/4, h/4, w/2, 0, 0, h, 0xFF0000FF, 0xFF00FF00, 0xFFFF0000);
  493. return dst;
  494. }
  495. Olivec_Canvas test_triangle_order_flip(void)
  496. {
  497. size_t w = 256;
  498. size_t h = 256;
  499. Olivec_Canvas dst = canvas_alloc(w, h);
  500. olivec_fill(dst, 0xFF181818);
  501. olivec_triangle3c(
  502. dst,
  503. w/4, h/4,
  504. 0, h,
  505. w, 0,
  506. 0xFF00FF00,
  507. 0xFFFF0000,
  508. 0xFF0000FF);
  509. return dst;
  510. }
  511. Test_Case test_cases[] = {
  512. DEFINE_TEST_CASE(fill_rect),
  513. DEFINE_TEST_CASE(fill_circle),
  514. DEFINE_TEST_CASE(draw_line),
  515. DEFINE_TEST_CASE(fill_triangle),
  516. DEFINE_TEST_CASE(alpha_blending),
  517. DEFINE_TEST_CASE(checker_example),
  518. DEFINE_TEST_CASE(circle_example),
  519. DEFINE_TEST_CASE(lines_example),
  520. DEFINE_TEST_CASE(hello_world_text_rendering),
  521. DEFINE_TEST_CASE(lines_circle),
  522. DEFINE_TEST_CASE(line_edge_cases),
  523. DEFINE_TEST_CASE(frame),
  524. DEFINE_TEST_CASE(sprite_blend),
  525. DEFINE_TEST_CASE(sprite_blend_out_of_bounds_cut),
  526. DEFINE_TEST_CASE(sprite_blend_flip),
  527. DEFINE_TEST_CASE(sprite_blend_flip_cut),
  528. DEFINE_TEST_CASE(sprite_blend_empty_rect),
  529. DEFINE_TEST_CASE(empty_rect),
  530. DEFINE_TEST_CASE(sprite_blend_null),
  531. DEFINE_TEST_CASE(sprite_blend_vs_copy),
  532. DEFINE_TEST_CASE(triangle_order_flip),
  533. DEFINE_TEST_CASE(barycentric_overflow)
  534. };
  535. #define TEST_CASES_COUNT (sizeof(test_cases)/sizeof(test_cases[0]))
  536. const char *shift(int *argc, char ***argv)
  537. {
  538. assert(*argc > 0);
  539. const char *result = *argv[0];
  540. *argc -= 1;
  541. *argv += 1;
  542. return result;
  543. }
  544. void list_available_tests(void)
  545. {
  546. fprintf(stderr, "Available tests:\n");
  547. for (size_t i = 0; i < TEST_CASES_COUNT; ++i) {
  548. fprintf(stderr, " %s\n", test_cases[i].id);
  549. }
  550. }
  551. Test_Case *find_test_case_by_id(const char *id)
  552. {
  553. for (size_t i = 0; i < TEST_CASES_COUNT; ++i) {
  554. if (strcmp(test_cases[i].id, id) == 0) {
  555. return &test_cases[i];
  556. }
  557. }
  558. return NULL;
  559. }
  560. typedef struct {
  561. int (*run)(const char *program_path, int argc, char **argv);
  562. const char *id;
  563. const char *description;
  564. } Subcmd;
  565. void usage(const char *program_path);
  566. int subcmd_run(const char *program_path, int argc, char **argv)
  567. {
  568. if (argc <= 0) {
  569. for (size_t i = 0; i < TEST_CASES_COUNT; ++i) {
  570. if (run_test_case(program_path, &test_cases[i]) == REPLAY_ERRORED) return(1);
  571. arena_reset(&default_arena);
  572. }
  573. } else {
  574. const char *test_case_id = shift(&argc, &argv);
  575. Test_Case *tc = find_test_case_by_id(test_case_id);
  576. if (tc == NULL) {
  577. list_available_tests();
  578. fprintf(stderr, "ERROR: could not find test case `%s`\n", test_case_id);
  579. return(1);
  580. }
  581. if (run_test_case(program_path, tc) == REPLAY_ERRORED) return(1);
  582. }
  583. return 0;
  584. }
  585. int subcmd_update(const char *program_path, int argc, char **argv)
  586. {
  587. UNUSED(program_path);
  588. if (argc <= 0) {
  589. for (size_t i = 0; i < TEST_CASES_COUNT; ++i) {
  590. if (!update_test_case(&test_cases[i])) return(1);
  591. arena_reset(&default_arena);
  592. }
  593. } else {
  594. const char *test_case_id = shift(&argc, &argv);
  595. Test_Case *tc = find_test_case_by_id(test_case_id);
  596. if (tc == NULL) {
  597. list_available_tests();
  598. fprintf(stderr, "ERROR: could not find test case `%s`\n", test_case_id);
  599. return(1);
  600. }
  601. if (!update_test_case(tc)) return(1);
  602. }
  603. return 0;
  604. }
  605. int subcmd_list(const char *program_path, int argc, char **argv)
  606. {
  607. UNUSED(program_path);
  608. UNUSED(argc);
  609. UNUSED(argv);
  610. list_available_tests();
  611. return 0;
  612. }
  613. int subcmd_help(const char *program_path, int argc, char **argv)
  614. {
  615. UNUSED(argc);
  616. UNUSED(argv);
  617. usage(program_path);
  618. return 0;
  619. }
  620. #define DEFINE_SUBCMD(name, desc) \
  621. { \
  622. .run = subcmd_##name, \
  623. .id = #name, \
  624. .description = desc, \
  625. }
  626. Subcmd subcmds[] = {
  627. DEFINE_SUBCMD(run, "Run the tests"),
  628. DEFINE_SUBCMD(update, "Update the tests"),
  629. DEFINE_SUBCMD(list, "List all available tests"),
  630. DEFINE_SUBCMD(help, "Print this help message"),
  631. };
  632. #define SUBCMDS_COUNT (sizeof(subcmds)/sizeof(subcmds[0]))
  633. Subcmd *find_subcmd_by_id(const char *id)
  634. {
  635. for (size_t i = 0; i < SUBCMDS_COUNT; ++i) {
  636. if (strcmp(subcmds[i].id, id) == 0) {
  637. return &subcmds[i];
  638. }
  639. }
  640. return NULL;
  641. }
  642. void usage(const char *program_path)
  643. {
  644. fprintf(stderr, "Usage: %s [Subcommand]\n", program_path);
  645. fprintf(stderr, "Subcommands:\n");
  646. int width = 0;
  647. for (size_t i = 0; i < SUBCMDS_COUNT; ++i) {
  648. int len = strlen(subcmds[i].id);
  649. if (width < len) width = len;
  650. }
  651. for (size_t i = 0; i < SUBCMDS_COUNT; ++i) {
  652. fprintf(stderr, " %-*s - %s\n", width, subcmds[i].id, subcmds[i].description);
  653. }
  654. }
  655. int main(int argc, char **argv)
  656. {
  657. int result = 0;
  658. {
  659. const char *program_path = shift(&argc, &argv);
  660. if (argc <= 0) {
  661. usage(program_path);
  662. fprintf(stderr, "ERROR: no subcommand is provided\n");
  663. return_defer(1);
  664. }
  665. const char *subcmd_id = shift(&argc, &argv);
  666. Subcmd *subcmd = find_subcmd_by_id(subcmd_id);
  667. if (subcmd != NULL) {
  668. return_defer(subcmd->run(program_path, argc, argv));
  669. } else {
  670. usage(program_path);
  671. fprintf(stderr, "ERROR: unknown subcommand `%s`\n", subcmd_id);
  672. return_defer(1);
  673. }
  674. }
  675. defer:
  676. arena_free(&default_arena);
  677. return result;
  678. }