From 93aa7e3aa291f3328cf34a5a2f86172f41d0affd Mon Sep 17 00:00:00 2001 From: cirakg Date: Sun, 18 Oct 2026 23:44:18 +0200 Subject: test: add cross-checked crypto/OTP vectors, unit tests and stricter screenshots - gen_vectors.py: RFC/FIPS/NIST vectors recomputed with Python's stdlib (openssl for AES) before use, plus seeded cases around SHA padding boundaries, 2038 and 2100; generates test/data/Vectors.mc. - 31 Monkey C tests (hashes, HMAC, PBKDF2, HKDF, AES, sealed records incl. every-byte tamper detection, Base32 errors, 100 OTP and 12 Steam vectors, clock, jobs) and a benchmark; pass on all four test devices. - Screenshot tolerance 0.01 %: one changed character no longer passes. - Golden images updated for version 0.2.0. Co-Authored-By: Claude Opus 5.5 --- test/util/UtilTest.mc | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 test/util/UtilTest.mc (limited to 'test/util/UtilTest.mc') diff --git a/test/util/UtilTest.mc b/test/util/UtilTest.mc new file mode 100644 index 0000000..ea689a6 --- /dev/null +++ b/test/util/UtilTest.mc @@ -0,0 +1,66 @@ +// Author: cirakg & claude ai +// Summary: Tests for the byte helpers, the 2038-safe clock widening and the job runner. + +import Toybox.Lang; +import Toybox.Test; + +(:test) +function bytesConversions(logger as Test.Logger) as Boolean { + return Bytes.toHex(Bytes.fromHex("00FFa5")).equals("00ffa5") + && Bytes.equal(Bytes.fromUtf8("é"), [0xc3, 0xa9]b) + && Bytes.fromHex("").size() == 0 && Bytes.toHex([]b).equals("") + && Bytes.equal(Bytes.concat([1]b, [2, 3]b), [1, 2, 3]b) + && Bytes.equal(Bytes.uint64BigEndian(1l), [0, 0, 0, 0, 0, 0, 0, 1]b) + && Bytes.equal(Bytes.uint64BigEndian(-1l), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]b) + && Bytes.equal(Bytes.uint64BigEndian(20000000000l / 30), [0, 0, 0, 0, 0x27, 0xbc, 0x86, 0xaa]b) + && Bytes.equal(Bytes.uint32BigEndian(0x01020304), [1, 2, 3, 4]b); +} + +(:test) +function bytesEqualWipeAndXor(logger as Test.Logger) as Boolean { + var a = [1, 2, 3]b; + var copy = Bytes.copy(a); + Bytes.xorInto(copy, [1, 2, 3]b); + var wiped = [5, 6]b; + Bytes.wipe(wiped); + Bytes.wipe(null); + return Bytes.equal(a, [1, 2, 3]b) && !Bytes.equal(a, [1, 2]b) && !Bytes.equal(a, [1, 2, 4]b) + && Bytes.equal(copy, [0, 0, 0]b) && Bytes.equal(wiped, [0, 0]b); +} + +// After 2038-01-19 the watch's 32-bit clock value is negative; read as +// unsigned it keeps counting up to 2106. +(:test) +function clockWidensTo64Bits(logger as Test.Logger) as Boolean { + return Clock.toUnsigned(0x7FFFFFFF) == 2147483647l + && Clock.toUnsigned(0x7FFFFFFF + 1) == 2147483648l + && Clock.toUnsigned(-1) == 4294967295l + && Clock.toUnsigned(0) == 0l + && Clock.nowUnix(0) > 1700000000l + && Clock.nowUnix(60) - Clock.nowUnix(0) >= 59l; +} + +(:test) +module JobFixtures { + class CountingJob extends Jobs.Job { + var done as Number = 0; + var calls as Number = 0; + + function initialize() { + Job.initialize(); + } + + function step(budget as Number) as Boolean { + calls++; + done += budget; + return done >= 10; + } + } +} + +(:test) +function jobsRunToCompletion(logger as Test.Logger) as Boolean { + var job = new JobFixtures.CountingJob(); + Jobs.runToCompletion(job, 3); + return job.calls == 4 && job.done == 12; +} -- cgit v1.2.3