140 lines
5.2 KiB
MonkeyC
140 lines
5.2 KiB
MonkeyC
import Toybox.System;
|
||
// Юнит-тесты ядра (спека api_specs/02-core.md): зоны, метрики подхода,
|
||
// таймер отдыха, гистерезис вибрации, клампы настроек, форматирование.
|
||
// Запуск — TestEntry (watch-app) в симуляторе; проверки — CoreChecks.
|
||
// Поле данных нельзя стартовать как приложение, поэтому тестовая сборка
|
||
// — отдельный манифест test-manifest.xml c watch-app-входом.
|
||
|
||
// ---------- ZoneCalc ----------
|
||
|
||
function zoneTanakaTest(c) {
|
||
var z = new ZoneCalc();
|
||
// возраст 45: (2080 − 315)/10 = 176,5 — используется точное значение ×10
|
||
c.eq(z.maxBpm10For(0, 45), 1765, "Tanaka age 45 -> 1765 (x10)");
|
||
c.eq(z.maxBpm10For(180, 45), 1800, "manual max has priority");
|
||
c.eq(z.maxBpm10For(0, 5), 1765, "age out of range -> default 45");
|
||
c.eq(z.maxBpm10For(null, null), 1765, "nulls -> default 45");
|
||
}
|
||
|
||
function zoneBoundariesTest(c) {
|
||
var z = new ZoneCalc();
|
||
// maxBpm 180 (maxBpm10 1800): 50 % = 90, 70 % = 126, 85 % = 153
|
||
c.eq(z.zoneFor(89, 1800), z.ZONE_BLUE, "89 -> blue");
|
||
c.eq(z.zoneFor(90, 1800), z.ZONE_GREEN, "90 -> green");
|
||
c.eq(z.zoneFor(125, 1800), z.ZONE_GREEN, "125 -> green");
|
||
c.eq(z.zoneFor(126, 1800), z.ZONE_YELLOW, "126 -> yellow");
|
||
c.eq(z.zoneFor(152, 1800), z.ZONE_YELLOW, "152 -> yellow");
|
||
c.eq(z.zoneFor(153, 1800), z.ZONE_RED, "153 -> red");
|
||
c.eq(z.zoneFor(null, 1800), z.ZONE_NONE, "null hr -> none");
|
||
c.eq(z.zoneFor(0, 1800), z.ZONE_NONE, "zero hr -> none");
|
||
}
|
||
|
||
// ---------- SetTracker ----------
|
||
|
||
function setAvgRoundingTest(c) {
|
||
var s = new SetTracker();
|
||
s.onSample(100);
|
||
s.onSample(101);
|
||
s.onSample(101);
|
||
c.eq(s.avgBpm(), 101, "100+101+101 -> 101 (round nearest)");
|
||
}
|
||
|
||
function setNullSkippedTest(c) {
|
||
var s = new SetTracker();
|
||
s.onSample(100);
|
||
s.onSample(null);
|
||
s.onSample(102);
|
||
c.eq(s.avgBpm(), 101, "null samples skipped -> 101");
|
||
}
|
||
|
||
function setLapResetsTest(c) {
|
||
var s = new SetTracker();
|
||
s.onSample(150);
|
||
c.eq(s.avgBpm(), 150, "before lap avg 150");
|
||
s.onLap(60000);
|
||
c.isNull(s.avgBpm(), "lap resets samples");
|
||
s.onSample(120);
|
||
c.eq(s.avgBpm(), 120, "after lap only new sample counted");
|
||
}
|
||
|
||
function setElapsedTest(c) {
|
||
var s = new SetTracker();
|
||
s.onLap(60000);
|
||
c.eq(s.elapsedMs(75500), 15500, "elapsed from lap border");
|
||
}
|
||
|
||
// ---------- RestTimer ----------
|
||
|
||
function restCountdownTest(c) {
|
||
var r = new RestTimer();
|
||
c.eq(r.isResting(60000), false, "before start not active -> false");
|
||
r.start(60000, 120);
|
||
c.eq(r.remainingMs(60000), 120000, "just started: full 120 s");
|
||
c.eq(r.remainingMs(120000), 60000, "half-way: 60 s left");
|
||
c.eq(r.remainingMs(180000), 0, "at end: 0");
|
||
c.eq(r.isResting(179999), true, "resting while remaining > 0");
|
||
c.eq(r.isResting(180000), false, "not resting at 0");
|
||
}
|
||
|
||
function restEndFiresOnceTest(c) {
|
||
var r = new RestTimer();
|
||
r.start(0, 120);
|
||
c.eq(r.onEnd(119000), false, "not ended yet");
|
||
c.eq(r.onEnd(120000), true, "fires at end");
|
||
c.eq(r.onEnd(121000), false, "fires only once");
|
||
r.start(130000, 60);
|
||
c.eq(r.onEnd(190000), true, "new start re-arms");
|
||
}
|
||
|
||
// ---------- Vibe (гистерезис красной зоны) ----------
|
||
|
||
function vibeRedHysteresisTest(c) {
|
||
var v = new Vibe();
|
||
c.eq(v.onZone(true, 0), true, "enter red -> vibrate");
|
||
c.eq(v.onZone(true, 30000), false, "still red <60 s -> no");
|
||
c.eq(v.onZone(true, 59999), false, "still red <60 s -> no");
|
||
c.eq(v.onZone(true, 61000), true, "still red >=60 s -> vibrate");
|
||
}
|
||
|
||
function vibeRedResetTest(c) {
|
||
var v = new Vibe();
|
||
c.eq(v.onZone(true, 0), true, "enter red -> vibrate");
|
||
c.eq(v.onZone(false, 10000), false, "leave red -> no vibrate");
|
||
c.eq(v.onZone(true, 20000), true, "re-enter red -> vibrate (reset)");
|
||
}
|
||
|
||
// ---------- Format ----------
|
||
|
||
function mmssTest(c) {
|
||
var f = new Format();
|
||
c.eq(f.mmss(0), "0:00", "0 -> 0:00");
|
||
c.eq(f.mmss(59999), "0:59", "59999 -> 0:59");
|
||
c.eq(f.mmss(60000), "1:00", "60000 -> 1:00");
|
||
c.eq(f.mmss(65000), "1:05", "65000 -> 1:05");
|
||
c.eq(f.mmss(600000), "10:00", "600000 -> 10:00");
|
||
c.eq(f.mmss(null), "0:00", "null -> 0:00");
|
||
c.eq(f.mmss(-5), "0:00", "negative -> 0:00");
|
||
}
|
||
|
||
function divTest(c) {
|
||
var f = new Format();
|
||
c.eq(f.div(5, 2), 2, "5/2 floor = 2");
|
||
c.eq(f.div(1, 2), 0, "1/2 floor = 0");
|
||
c.eq(f.div(600000, 1000), 600, "600000/1000 = 600");
|
||
}
|
||
|
||
// ---------- FieldSettings (клампы) ----------
|
||
|
||
function clampTest(c) {
|
||
var s = new FieldSettings();
|
||
c.eq(s.clamp(10, 30, 600, 120), 120, "rest below min -> default");
|
||
c.eq(s.clamp(600, 30, 600, 120), 600, "rest max ok");
|
||
c.eq(s.clamp(30, 30, 600, 120), 30, "rest min ok");
|
||
c.eq(s.clamp(null, 30, 600, 120), 120, "null -> default");
|
||
c.eq(s.clamp("120", 30, 600, 120), 120, "string -> default");
|
||
c.eq(s.clamp(-5, 0, 250, 0), 0, "hr below min -> default 0");
|
||
c.eq(s.clamp(250, 0, 250, 0), 250, "hr max ok");
|
||
c.eq(s.clamp(200, 14, 100, 45), 45, "age above max -> default");
|
||
c.eq(s.clamp(14, 14, 100, 45), 14, "age min ok");
|
||
}
|