aboutsummaryrefslogtreecommitdiff
path: root/test/import/FuzzTest.mc
blob: fcad345e857ef7470de9184ba6c82d2eb5f53148 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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.item(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;
}