aboutsummaryrefslogtreecommitdiff
path: root/test/import/FuzzTest.mc
diff options
context:
space:
mode:
Diffstat (limited to 'test/import/FuzzTest.mc')
-rw-r--r--test/import/FuzzTest.mc107
1 files changed, 107 insertions, 0 deletions
diff --git a/test/import/FuzzTest.mc b/test/import/FuzzTest.mc
new file mode 100644
index 0000000..7573ffa
--- /dev/null
+++ b/test/import/FuzzTest.mc
@@ -0,0 +1,107 @@
+// Author: cirakg & claude ai
+// Summary: Runs the seeded fuzz corpus through the tokenizer, the Aegis reader and the importer: always a result or a typed error, never a crash.
+//
+// README 17.3. Each input must end in a definite state within a bounded
+// number of steps (no endless loop), and anything the importer stores must
+// be a valid entry. Watchdog limits aren't enforced in unit tests; the
+// per-step bounds are covered by the UI flow on the Forerunner 255S.
+
+import Toybox.Lang;
+import Toybox.Test;
+
+(:test)
+function fuzzTokenizerAlwaysTerminates(logger as Test.Logger) as Boolean {
+ var inputs = FuzzCorpus.JSON;
+ for (var i = 0; i < inputs.size(); i++) {
+ var data = Bytes.fromHex(inputs[i] as String);
+ var tok = new Json.Tokenizer(data);
+ var finished = false;
+ // Every token consumes at least one byte, so size + 2 calls suffice.
+ for (var n = 0; n < data.size() + 2; n++) {
+ var t = tok.next();
+ if (t == Json.T_END || t == Json.T_INCOMPLETE || t == Json.T_ERROR) {
+ finished = true;
+ break;
+ }
+ }
+ if (!finished) {
+ logger.error("tokenizer didn't finish on input " + i);
+ return false;
+ }
+ }
+ return true;
+}
+
+(:test)
+function fuzzAegisReaderGivesDefiniteResults(logger as Test.Logger) as Boolean {
+ var inputs = FuzzCorpus.JSON;
+ var done = 0;
+ for (var i = 0; i < inputs.size(); i++) {
+ var data = Bytes.fromHex(inputs[i] as String);
+ var reader = new AegisReader(data);
+ var status = AegisReader.RUNNING;
+ for (var n = 0; n < data.size() + 2 && status == AegisReader.RUNNING; n++) {
+ status = reader.step();
+ }
+ if (status == AegisReader.RUNNING || (status == AegisReader.FAILED && reader.error == null)) {
+ logger.error("input " + i + ": status " + status);
+ return false;
+ }
+ if (status == AegisReader.DONE) {
+ done++;
+ for (var k = 0; k < reader.items.size(); k++) {
+ var item = reader.items[k];
+ var e = item.entry;
+ if (e == null ? item.problem == null && item.skipped == null : e.validate() != null) {
+ logger.error("input " + i + " item " + k + " is neither valid nor explained");
+ return false;
+ }
+ }
+ }
+ }
+ logger.debug("FUZZ aegis inputs=" + inputs.size() + " complete=" + done);
+ return done > 0;
+}
+
+// The whole importer on every input that is valid text: a report that
+// explains everything, and only valid entries in the vault.
+(:test)
+function fuzzImporterNeverStoresInvalidEntries(logger as Test.Logger) as Boolean {
+ var all = [] as Array<String>;
+ all.addAll(FuzzCorpus.JSON as Array<String>);
+ all.addAll(FuzzCorpus.URIS as Array<String>);
+ var imported = 0;
+ for (var i = 0; i < all.size(); i++) {
+ var text = Bytes.toUtf8(Bytes.fromHex(all[i]));
+ if (text == null) {
+ continue;
+ }
+ var vault = new Vault(new MemoryBackend(), new FakeTime());
+ var job = new Importer(vault, text, Importer.MODE_APPEND, false, 1800000000l);
+ var finished = false;
+ for (var n = 0; n < 5000; n++) {
+ if (job.step(1)) {
+ finished = true;
+ break;
+ }
+ }
+ var r = job.report;
+ if (!finished || (r.failure == null && r.waitingChars < 0 && r.summary().find("Imported") != 0)) {
+ logger.error("input " + i + " didn't finish cleanly: " + r.summary());
+ return false;
+ }
+ var ids = vault.ids();
+ for (var k = 0; k < ids.size(); k++) {
+ var e = vault.open(ids[k]);
+ var problem = e.validate();
+ e.wipeSecrets();
+ if (problem != null) {
+ logger.error("input " + i + " stored an invalid entry: " + problem);
+ return false;
+ }
+ }
+ imported += ids.size();
+ }
+ logger.debug("FUZZ importer inputs=" + all.size() + " entries_stored=" + imported);
+ return imported > 0;
+}