aboutsummaryrefslogtreecommitdiff
path: root/test/settings/CodeFormTest.mc
blob: 9d51797acf3f1d7373a5ac39445124b705cc5323 (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Author:  cirakg & claude ai
// Summary: Tests for the Add code form: a valid and invalid payload for every symbology, options, notes, size warnings and the cache.

import Toybox.Lang;
import Toybox.Test;

(:test)
module CodeFormCases {

    // [symbology, payload, part of the status] for contents every
    // symbology accepts (all fake).
    const VALID = [
        [Codes.QR, "https://example.com/card/0042", ""],
        [Codes.CODE128, "Member-0042", ""],
        [Codes.CODE39, "CARD 0042", ""],
        [Codes.EAN13, " 590123412345 ", "check digit 7 added"],
        [Codes.EAN8, "96385074", ""],
        [Codes.UPCA, "03600029145", "check digit 2 added"],
        [Codes.UPCE, "0123456", ""],
        [Codes.ITF, "123456789", ""], // with its check digit: 10 digits
        [Codes.CODABAR, "40156", "start and stop characters A...A added"],
        [Codes.DATAMATRIX, "PARCEL-FAKE-0042", ""],
        [Codes.PDF417, "Boarding pass FAKE 0042", ""],
        [Codes.AZTEC, "RAIL-FAKE-0042", ""]
    ];

    // [symbology, payload, check-digit mode, expected status part].
    function invalid() as Array<Array> {
        return [
            [Codes.EAN13, "5901234123450", Codes.CHECK_ADD_VERIFY,
                "Not added: EAN-13: check digit should be 7, got 0. Choose 'Fix if wrong' to correct it"],
            [Codes.EAN13, "59012341234", Codes.CHECK_ADD_VERIFY, "EAN-13 needs 12 or 13 digits, got 11"],
            [Codes.EAN13, "590123412345", Codes.CHECK_OFF, "EAN-13 with the check digit off needs all 13 digits"],
            [Codes.EAN8, "12A4567", Codes.CHECK_ADD_VERIFY, "EAN-8 takes digits only"],
            [Codes.UPCA, "123", Codes.CHECK_ADD_VERIFY, "UPC-A needs 11 or 12 digits, got 3"],
            [Codes.UPCE, "2123456", Codes.CHECK_ADD_VERIFY, "UPC-E: the number system must be 0 or 1, got 2"],
            [Codes.CODE128, "Café", Codes.CHECK_ADD_VERIFY, "Code 128: 'é' (position 4) is not a printable ASCII character"],
            [Codes.CODE39, "card", Codes.CHECK_ADD_VERIFY, "Code 39: 'c' (position 1)"],
            [Codes.ITF, "123", Codes.CHECK_OFF, "ITF needs an even number of digits: add a leading 0"],
            [Codes.CODABAR, "A12X3B", Codes.CHECK_ADD_VERIFY, "Codabar: 'X' isn't allowed there"],
            [Codes.QR, SettingsFixtures.repeat("a", 2400), Codes.CHECK_ADD_VERIFY, "QR: too long for a QR code"],
            [Codes.DATAMATRIX, SettingsFixtures.repeat("a", 1600), Codes.CHECK_ADD_VERIFY, "too long for Data Matrix"],
            [Codes.PDF417, SettingsFixtures.repeat("a1", 1500), Codes.CHECK_ADD_VERIFY, "PDF417: too"],
            [Codes.AZTEC, SettingsFixtures.repeat("é", 2000), Codes.CHECK_ADD_VERIFY, "Aztec: too long"]
        ] as Array<Array>;
    }
}

// Every symbology accepts a typical payload; 2D codes are cached encoded.
(:test)
function settingsAddsEverySymbology(logger as Test.Logger) as Boolean {
    var vault = SettingsFixtures.vault();
    var s = SettingsFixtures.store();
    var ok = true;
    for (var i = 0; i < CodeFormCases.VALID.size(); i++) {
        var c = CodeFormCases.VALID[i] as Array;
        var symbology = c[0] as Number;
        SettingsFixtures.code(s, symbology, "Card " + i, c[1] as String);
        SettingsFixtures.run(vault, s);
        var status = SettingsFixtures.text(s, CodeForm.STATUS);
        var ids = vault.ids();
        if (ids.size() != i + 1 || status.find("Added " + ids[i] + " Card " + i) != 0 || status.find(c[2] as String) == null) {
            logger.error(Codes.name(symbology) + ": " + status);
            ok = false;
            continue;
        }
        var cached = vault.loadCodeCache(ids[i]);
        if (Codes.isMatrix(symbology) != (cached != null && Codes.Matrix.fromBytes(cached) != null)) {
            logger.error(Codes.name(symbology) + ": cache " + (cached != null));
            ok = false;
        }
        if (!SettingsFixtures.text(s, CodeForm.PAYLOAD).equals("") || Forms.flag(s, CodeForm.SWITCH)) {
            logger.error(Codes.name(symbology) + ": form not cleared");
            ok = false;
        }
    }
    // Linear contents are stored trimmed.
    return ok && SettingsFixtures.secretOf(vault, vault.ids()[3]).equals("590123412345");
}

// Every symbology's typical mistakes give the encoder's message; nothing
// is added and only the contents are cleared.
(:test)
function settingsRejectsInvalidCodes(logger as Test.Logger) as Boolean {
    var vault = SettingsFixtures.vault();
    var cases = CodeFormCases.invalid();
    var ok = true;
    for (var i = 0; i < cases.size(); i++) {
        var c = cases[i];
        var s = SettingsFixtures.store();
        SettingsFixtures.code(s, c[0] as Number, "Card", c[1] as String);
        s.set("codeCheckDigit", c[2] as Number);
        SettingsFixtures.run(vault, s);
        var status = SettingsFixtures.text(s, CodeForm.STATUS);
        if (status.find(c[3] as String) == null || vault.ids().size() != 0 || !SettingsFixtures.text(s, "codeName").equals("Card")
                || !SettingsFixtures.text(s, CodeForm.PAYLOAD).equals("")) {
            logger.error("case " + i + " (" + Codes.name(c[0] as Number) + "): " + status);
            ok = false;
        }
    }
    return ok;
}

(:test)
function settingsChecksCodeFields(logger as Test.Logger) as Boolean {
    var vault = SettingsFixtures.vault();
    var s = SettingsFixtures.store();
    SettingsFixtures.code(s, Codes.QR, " ", "x");
    SettingsFixtures.run(vault, s);
    var ok = SettingsFixtures.contains(logger, s, CodeForm.STATUS, "Name: enter a name for the card");
    SettingsFixtures.code(s, Codes.QR, "Card", "  ");
    SettingsFixtures.run(vault, s);
    ok = ok && SettingsFixtures.contains(logger, s, CodeForm.STATUS, "Contents: missing");
    SettingsFixtures.code(s, 12, "Card", "x");
    SettingsFixtures.run(vault, s);
    ok = ok && SettingsFixtures.contains(logger, s, CodeForm.STATUS, "Type: choose one of the 12 barcode types");
    SettingsFixtures.code(s, Codes.QR, "Card", SettingsFixtures.repeat("é", 2049));
    SettingsFixtures.run(vault, s);
    return ok && SettingsFixtures.contains(logger, s, CodeForm.STATUS, "Contents: too long (4098 bytes, at most 4096)")
        && vault.ids().size() == 0;
}

// The form's options end up in Entry.options; "Fix if wrong" corrects the
// check digit and says so.
(:test)
function settingsStoresCodeOptions(logger as Test.Logger) as Boolean {
    var vault = SettingsFixtures.vault();
    var s = SettingsFixtures.store();
    SettingsFixtures.code(s, Codes.EAN13, "Gym", "5901234123450");
    s.set("codeCheckDigit", Codes.CHECK_FIX);
    s.set("codeShowText", false);
    s.set("codeEcLevel", 3);
    s.set("codeGroup", "Sport");
    s.set("codeLocked", false);
    SettingsFixtures.run(vault, s);
    if (vault.ids().size() != 1) {
        logger.error(SettingsFixtures.text(s, CodeForm.STATUS));
        return false;
    }
    var e = vault.open(vault.ids()[0]);
    var ok = e.options == (3 | (Codes.CHECK_FIX << Codes.OPT_CHECK_SHIFT)) && e.group.equals("Sport") && e.type == Codes.EAN13;
    e.wipeSecrets();
    return ok && SettingsFixtures.contains(logger, s, CodeForm.STATUS, "check digit fixed: 7 (was 0)");
}

// Codes that encode but can't be shown at the minimum module size on this
// screen are added with the README 12.2 warning.
(:test)
function settingsWarnsWhenACodeIsTooLarge(logger as Test.Logger) as Boolean {
    var vault = SettingsFixtures.vault();
    var s = SettingsFixtures.store();
    // About version 39 at level L: over 360 px at 2 px per module.
    SettingsFixtures.code(s, Codes.QR, "Big", SettingsFixtures.repeat("Ab", 1400));
    s.set("codeEcLevel", 0);
    SettingsFixtures.run(vault, s);
    var qr = SettingsFixtures.contains(logger, s, CodeForm.STATUS, "Too large for this screen: needs 2 px per module");
    SettingsFixtures.code(s, Codes.CODE128, "Wide", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn");
    SettingsFixtures.run(vault, s);
    return qr && SettingsFixtures.contains(logger, s, CodeForm.STATUS, "Too large for this screen: needs") && vault.ids().size() == 2;
}

// A locked card sent while locked waits as pending; no cache is written
// for it (its key isn't available).
(:test)
function settingsLockedCodeWaitsForThePin(logger as Test.Logger) as Boolean {
    var vault = SettingsFixtures.vault();
    StorageFixtures.setPin(vault, StorageFixtures.PIN);
    vault.lock();
    var s = SettingsFixtures.store();
    SettingsFixtures.code(s, Codes.DATAMATRIX, "Parcel", "PARCEL-FAKE-0042");
    s.set("codeLocked", true);
    SettingsFixtures.run(vault, s);
    var id = vault.pendingIds().size() == 1 ? vault.pendingIds()[0] : "";
    return id.length() > 0 && vault.storage().getValue("x." + id) == null
        && SettingsFixtures.contains(logger, s, CodeForm.STATUS, "It waits for your PIN on the watch");
}