shithub: puzzles

Download patch

ref: 53a1faa0d758c86c5bc77808c4265129d627be1b
parent: 9478efe32ea5aa6577d11fe45df81b49199780cd
author: Ben Harris <bjh21@bjh21.me.uk>
date: Sat Jan 7 13:57:48 EST 2023

Add a fuzzing harness for Puzzles

This just feeds save files into the loading code, but because of how
Puzzles is structured that actually exercises most of its parsers.

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -275,4 +275,9 @@
 cliprogram(sort-test sort.c COMPILE_DEFINITIONS SORT_TEST)
 cliprogram(tree234-test tree234.c COMPILE_DEFINITIONS TEST)
 
+write_generated_games_header()
+cliprogram(fuzzpuzz fuzzpuzz.c list.c ${puzzle_sources}
+  COMPILE_DEFINITIONS COMBINED)
+target_include_directories(fuzzpuzz PRIVATE ${generated_include_dir})
+
 build_extras()
--- /dev/null
+++ b/fuzzpuzz.c
@@ -1,0 +1,65 @@
+/*
+ * fuzzpuzz.c: Fuzzing frontend to all puzzles.
+ */
+
+/*
+ * The idea here is that this front-end supports all back-ends and can
+ * feed them save files.  This tests the deserialiser, the code for
+ * loading game descriptions, and the processing of move strings,
+ * without all the tedium of actually rendering anything.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "puzzles.h"
+
+static bool savefile_read(void *wctx, void *buf, int len)
+{
+    FILE *fp = (FILE *)wctx;
+    int ret;
+
+    ret = fread(buf, 1, len, fp);
+    return (ret == len);
+}
+
+int main(int argc, char **argv)
+{
+    const char *err;
+    char *gamename;
+    int i;
+    const game * ourgame = NULL;
+    midend *me;
+
+    if (argc != 1) {
+        fprintf(stderr, "usage: %s\n", argv[0]);
+        exit(1);
+    }
+
+    err = identify_game(&gamename, savefile_read, stdin);
+    if (err != NULL) {
+        fprintf(stderr, "%s\n", err);
+        exit(1);
+    }
+
+    for (i = 0; i < gamecount; i++)
+        if (strcmp(gamename, gamelist[i]->name) == 0)
+            ourgame = gamelist[i];
+    if (ourgame == NULL) {
+        fprintf(stderr, "Game '%s' not recognised\n", gamename);
+        exit(1);
+    }
+
+    me = midend_new(NULL, ourgame, NULL, NULL);
+
+    rewind(stdin);
+    err = midend_deserialise(me, savefile_read, stdin);
+    if (err != NULL) {
+        fprintf(stderr, "%s\n", err);
+        exit(1);
+    }
+    midend_free(me);
+    return 0;
+}