-- Kør: luajit tests/test_adFieldModel.lua (fra repo-roden) dofile("FS25_ADSmartPickup/adFieldModel.lua") local M = ADFieldModel local failures = 0 local function check(name, actual, expected) if actual == expected then print("OK " .. name) else failures = failures + 1 print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) end end -- M1: ny mark = venter, manuel ensilage uden riv local f = M.newField(109) check("M1 stage", f.stage, "idle") check("M1 mode", f.plan.mode, "manual") check("M1 product auto", f.plan.product, "auto") check("M1 auto = ensilage på slåafgrøde", M.product(f.plan, "mowable"), "silage") check("M1 auto = høst ellers", M.product(f.plan, "harvest"), "harvest") check("M1 rake", f.plan.rake, false) -- M2: klar ved >= 90 % check("M2 89 % ikke klar", M.onReadiness(f, 0.89).stage, "idle") local ready = M.onReadiness(f, 0.9) check("M2 90 % klar", ready.stage, "ready") check("M2 immutabel", f.stage, "idle") -- M3: klar mark slået af spilleren -> tilbage til venter check("M3 slået manuelt", M.onReadiness(ready, 0.3).stage, "idle") check("M3 lidt under 90 % forbliver klar", M.onReadiness(ready, 0.8).stage, "ready") -- M4: trin pr. plan check("M4 ensilage", table.concat(M.workStages({product = "silage"}), ","), "mowing,collecting") check("M4 ensilage m. riv", table.concat(M.workStages({product = "silage", rake = true}), ","), "mowing,raking,collecting") check("M4 hø", table.concat(M.workStages({product = "hay", rake = false}), ","), "mowing,tedding,collecting") check("M4 hø m. riv", table.concat(M.workStages({product = "hay", rake = true}), ","), "mowing,tedding,raking,collecting") -- M5: start: auto starter selv, manuel kun på forespørgsel check("M5 manuel starter ikke", M.shouldStart(ready), false) local auto = M.onReadiness(M.newField(5), 1) auto.plan.mode = "auto" check("M5 auto starter", M.shouldStart(auto), true) local requested = M.newField(6); requested.stage = "ready"; requested.startRequested = true check("M5 Start nu", M.shouldStart(requested), true) local started = M.start(ready) check("M5 start -> slås", started.stage, "mowing") check("M5 start rydder forespørgsel", started.startRequested, false) local oldMow = M.with(ready, {mowVehicleId = "old"}) check("M5 start glemmer gammel slåmaskine", M.start(oldMow).mowVehicleId, nil) -- M6: videre gennem trinene; færdig mark bliver klar igen når den er vokset local hay = M.start(M.onReadiness(M.newField(7), 1)); hay.plan.product = "hay" hay = M.advance(hay); check("M6 hø -> vendes", hay.stage, "tedding") hay = M.advance(hay); check("M6 -> samles op", hay.stage, "collecting") hay = M.advance(hay); check("M6 -> færdig", hay.stage, "done") check("M6 færdig forbliver ved lav andel", M.onReadiness(hay, 0.1).stage, "done") check("M6 færdig -> klar når vokset", M.onReadiness(hay, 0.95).stage, "ready") check("M6 arbejdende mark ignorerer andel", M.onReadiness(started, 0).stage, "mowing") check("M6 isWorking", M.isWorking(started), true) check("M6 stop -> klar", M.stop(started).stage, "ready") -- M7: roller, maks sæt, bredde check("M7 rolle slå", M.roleFor("mowing"), "mower") check("M7 rolle saml", M.roleFor("collecting"), "collector") check("M7 rolle klar = ingen", M.roleFor("ready"), nil) check("M7 maks læssevogne = planens sæt", M.maxRigs("collecting", {rigs = 2}), 2) check("M7 maks slå", M.maxRigs("mowing"), 1) local okWide, whyWide = M.checkWidth("mower", 60, nil) check("M7 over 50 m", okWide, false); check("M7 grund", whyWide, "tooWide") check("M7 40,8 m slå ok", (M.checkWidth("mower", 40.8, nil)), true) local okNarrow, whyNarrow, args = M.checkWidth("collector", 20, 40.8) check("M7 for smal", okNarrow, false); check("M7 grund smal", whyNarrow, "tooNarrow"); check("M7 args", args[2], 40.8) check("M7 lige bred ok (tolerance)", (M.checkWidth("collector", 40.76, 40.8)), true) check("M7 ukendt slåbredde ok", (M.checkWidth("tedder", 10, nil)), true) -- wantsRig: kun arbejdende trin, under maks, efter pause check("M7 wantsRig slå", M.wantsRig(started, 0, 0).role, "mower") check("M7 wantsRig fuld", M.wantsRig(started, 1, 0), nil) local paused = M.start(M.onReadiness(M.newField(8), 1)); paused.retryAt = 1000 check("M7 wantsRig i pause", M.wantsRig(paused, 0, 500), nil) check("M7 wantsRig efter pause", M.wantsRig(paused, 0, 1000).role, "mower") check("M7 wantsRig klar = nil", M.wantsRig(ready, 0, 0), nil) -- M8: gem/indlæs bevarer trin, plan og slåbredde local saved = M.advance(M.start(M.onReadiness(M.newField(109), 1))); saved.mowWidth = 40.8; saved.mowVehicleId = "vehicleBigM" saved.plan = {mode = "auto", product = "hay", rake = true, storeBuildingIds = {"b12", "b7"}} local back = M.fromRecord(M.toRecord(saved)) check("M8 id", back.id, 109) check("M8 stage", back.stage, saved.stage) check("M8 mode", back.plan.mode, "auto") check("M8 product", back.plan.product, "hay") check("M8 rake", back.plan.rake, true) check("M8 lager", table.concat(back.plan.storeBuildingIds, " "), "b12 b7") check("M8 slåbredde", back.mowWidth, 40.8) check("M8 slåmaskine", back.mowVehicleId, "vehicleBigM") check("M8 ingen slåmaskine", M.fromRecord({id = 4, stage = "idle"}).mowVehicleId, nil) check("M8 ukendt trin -> venter", M.fromRecord({id = 3, stage = "bogus"}).stage, "idle") -- M9: with() kopierer og fletter planen local base = M.start(M.onReadiness(M.newField(1), 1)) local noted = M.with(base, {reason = {text = "x"}, retryAt = 5, plan = {product = "hay"}}) check("M9 grund", noted.reason.text, "x"); check("M9 pause", noted.retryAt, 5) check("M9 plan flettet", noted.plan.product, "hay"); check("M9 mode bevaret", noted.plan.mode, "manual") check("M9 trin bevaret", noted.stage, "mowing"); check("M9 original urørt", base.plan.product, "auto") -- M10: Start nu på en mark der ikke er helt klar -> klar; arbejdende mark røres ikke check("M10 venter -> klar", M.forceReady(M.newField(2)).stage, "ready") check("M10 arbejdende uændret", M.forceReady(base).stage, "mowing") -- M11: krævet bredde og kursus-kilde pr. rolle (smalle standardmaskiner, 25/9) local cut = M.with(M.newField(3), {mowWidth = 40.8}) check("M11 vender kræver ingen bredde", M.requiredWidth(cut, "tedder"), nil) check("M11 river kræver ingen bredde", M.requiredWidth(cut, "rake"), nil) check("M11 læssevogn uden riv = slåbredde", M.requiredWidth(cut, "collector"), 40.8) local raked = M.with(cut, {plan = {rake = true}}) check("M11 læssevogn efter riv = ingen krav", M.requiredWidth(raked, "collector"), nil) check("M11 vender eget kursus", M.courseSource(cut, "tedder"), nil) check("M11 river eget kursus", M.courseSource(raked, "rake"), nil) check("M11 læssevogn uden riv -> slåkursus", M.courseSource(cut, "collector"), "mower") check("M11 læssevogn efter riv -> rivekursus", M.courseSource(raked, "collector"), "rake") check("M11 slåmaskine eget kursus", M.courseSource(cut, "mower"), nil) -- gem/indlæs rivemaskine local withRake = M.with(raked, {rakeVehicleId = "vehicleRake"}) check("M11 rivemaskine gemt", M.fromRecord(M.toRecord(withRake)).rakeVehicleId, "vehicleRake") check("M11 start glemmer rivemaskine", M.start(M.with(M.onReadiness(M.newField(9), 1), {rakeVehicleId = "x"})).rakeVehicleId, nil) -- M12: sideriver — læssevognens CP-forskydning = egen forskydning − skårets forskydning (venstre positiv), -- holdt inden for Courseplays ±10 m; skårets forskydning gemmes pr. mark check("M12 midterrive", M.collectorOffset(0, 0), 0) check("M12 skår 3 m til venstre", M.collectorOffset(0, 3), -3) check("M12 vogn med egen forskydning", M.collectorOffset(0.5, -2), 2.5) check("M12 holdes inden for 10 m", M.collectorOffset(0, 14), -10) check("M12 ukendt skår -> egen forskydning", M.collectorOffset(0.4, nil), 0.4) local side = M.with(M.newField(11), {windrowOffset = 3.2}) check("M12 skår gemt", M.fromRecord(M.toRecord(side)).windrowOffset, 3.2) check("M12 intet skår gemt", M.fromRecord(M.toRecord(M.newField(12))).windrowOffset, nil) check("M12 start glemmer skår", M.start(M.with(M.onReadiness(M.newField(13), 1), {windrowOffset = 1})).windrowOffset, nil) -- M13: gæringssilo = produktion der laver markens skår om til ensilage (FermentingSiloPack m.fl.) local GRASS_W, DRY_W, SILAGE, CHAFF, BREAD = 10, 11, 20, 30, 40 local fermenter = {{inputs = {{type = CHAFF}}, outputs = {{type = SILAGE}}}, {inputs = {{type = GRASS_W}}, outputs = {{type = SILAGE}}}} check("M13 græs -> ensilage", M.isFermenter(fermenter, GRASS_W, SILAGE), true) check("M13 hø ikke i denne", M.isFermenter(fermenter, DRY_W, SILAGE), false) check("M13 bageri ikke", M.isFermenter({{inputs = {{type = GRASS_W}}, outputs = {{type = BREAD}}}}, GRASS_W, SILAGE), false) check("M13 ingen produktioner", M.isFermenter(nil, GRASS_W, SILAGE), false) check("M13 ukendt ensilage-type", M.isFermenter(fermenter, GRASS_W, nil), false) -- M14: høstkæde + klargøring efter behov local plan = {mode = "manual", product = "auto", rake = false, rigs = 1, unloaders = 1, mulch = false, lime = true, soil = "auto", sow = true, sowFruit = "", roll = true, fertilize = true, weed = true, storeBuildingIds = {}} check("M14 høst + kultivér + så + tromle", table.concat(M.workStages(plan, "harvest", {}), ","), "harvesting,cultivating,sowing,rolling") check("M14 kalk + pløj når jorden trænger", table.concat(M.workStages(plan, "harvest", {lime = true, plow = true}), ","), "harvesting,liming,plowing,sowing,rolling") local noSoil = {soil = "none", sow = false, lime = false, mulch = true} check("M14 kun stubbehakning", table.concat(M.workStages(noSoil, "harvest", {}), ","), "harvesting,mulching") check("M14 bar mark = kun klargøring", table.concat(M.workStages(plan, "bare", {plow = true}), ","), "plowing,sowing,rolling") check("M14 tvunget pløj", table.concat(M.prepStages({soil = "plow"}, {}), ","), "plowing") local harvestField = M.start(M.with(M.newField(20), {plan = {sow = true, soil = "cultivate"}}), "harvest", {}) check("M14 start = høst", harvestField.stage, "harvesting") check("M14 husker art", harvestField.kind, "harvest") local afterHarvest = M.advance(harvestField, {}) check("M14 høst -> kultivér", afterHarvest.stage, "cultivating") check("M14 -> så", M.advance(afterHarvest, {}).stage, "sowing") check("M14 så -> færdig", M.advance(M.advance(afterHarvest, {}), {}).stage, "done") check("M14 kalk springes over når jorden er kalket", M.advance(M.with(harvestField, {plan = {lime = true}}), {lime = false}).stage, "cultivating") check("M14 tom kæde -> færdig", M.start(M.with(M.newField(21), {plan = {soil = "none", sow = false, lime = false}}), "bare", {}).stage, "done") -- M15: hjælpere under høst og multi-tool-baner check("M15 aflæsningsvogn efter høsteren", M.wantsRig(harvestField, {primary = 1, helpers = 0}, 0).role, "unloader") check("M15 hjælper-flag", M.wantsRig(harvestField, {primary = 1, helpers = 0}, 0).helper, true) check("M15 alle vogne på plads -> intet", M.wantsRig(harvestField, {primary = 1, helpers = 1}, 0), nil) local multi = M.with(harvestField, {plan = {rigs = 3, unloaders = 2}}) check("M15 multi: 3 høstere samlet", M.wantsRig(multi, {primary = 0, helpers = 0}, 0).count, 3) check("M15 multi: 2 vogne", M.maxHelpers("harvesting", multi.plan), 2) check("M15 ingen hjælper ved slåning", M.maxHelpers("mowing", multi.plan), 0) check("M15 baner 2", table.concat(M.lanePositions(2), ","), "-1,1") check("M15 baner 3", table.concat(M.lanePositions(3), ","), "-1,0,1") check("M15 baner 4", table.concat(M.lanePositions(4), ","), "-2,-1,1,2") check("M15 baner 5", table.concat(M.lanePositions(5), ","), "-2,-1,0,1,2") check("M15 over 5 -> 5", #M.lanePositions(9), 5) check("M15 maxRigs holdes 1-5", M.maxRigs("mowing", {rigs = 7}), 5) -- M16: pleje under vækst local growing = {kind = "harvest", growing = true, fert = 1, fertMax = 2, lime = 2, weed = 0} local idleField = M.newField(30) check("M16 gødning", M.careNeed(idleField, growing), "fertilizing") check("M16 ukrudt før gødning", M.careNeed(idleField, {kind = "harvest", growing = true, fert = 1, fertMax = 2, lime = 2, weed = 3}), "weeding") check("M16 græs ukrudtsbehandles ikke", M.careNeed(idleField, {kind = "mowable", growing = true, fert = 2, fertMax = 2, lime = 2, weed = 3}), nil) check("M16 kalk", M.careNeed(idleField, {kind = "harvest", growing = true, fert = 2, fertMax = 2, lime = 0, weed = 0}), "careLiming") check("M16 intet behov", M.careNeed(idleField, {kind = "harvest", growing = true, fert = 2, fertMax = 2, lime = 1, weed = 1}), nil) check("M16 ikke voksende", M.careNeed(idleField, {kind = "harvest", growing = false, fert = 0, fertMax = 2}), nil) check("M16 gødning slået fra", M.careNeed(M.with(idleField, {plan = {fertilize = false}}), growing), nil) check("M16 arbejdende mark", M.careNeed(harvestField, growing), nil) local cared = M.startCare(idleField, "fertilizing") check("M16 pleje-trin", cared.stage, "fertilizing") check("M16 rolle", M.roleFor(cared.stage), "fertilizer") check("M16 kalk-pleje rolle", M.roleFor("careLiming"), "limer") check("M16 pleje -> idle", M.advance(cared, {}).stage, "idle") local twice = M.startCare(M.advance(cared, {}), "fertilizing") check("M16 højst 2 gange", M.careNeed(M.advance(twice, {}), growing), nil) check("M16 ny cyklus nulstiller", M.careNeed(M.advance(M.start(M.advance(twice, {}), "harvest", {}), {}), growing) ~= "x", true) check("M16 start nulstiller pleje-tæller", next(M.start(twice, "harvest", {}).careRuns), nil) check("M16 stop af pleje -> idle", M.stop(cared).stage, "idle") check("M16 stop af høst -> klar", M.stop(harvestField).stage, "ready") -- M17: såafgrøde og gem/indlæs af den udvidede plan check("M17 så samme afgrøde", M.sowFruit(M.with(M.newField(40), {lastFruit = "WHEAT"})), "WHEAT") check("M17 planens afgrøde vinder", M.sowFruit(M.with(M.newField(40), {lastFruit = "WHEAT", plan = {sowFruit = "CANOLA"}})), "CANOLA") local full = M.with(M.startCare(M.newField(41), "weeding"), {kind = "harvest", lastFruit = "BARLEY", plan = {rigs = 3, unloaders = 2, mulch = true, lime = false, soil = "plow", sow = true, sowFruit = "WHEAT", roll = true, fertilize = false, weed = false, product = "harvest"}}) local round = M.fromRecord(M.toRecord(full)) check("M17 trin", round.stage, "weeding") check("M17 sæt", round.plan.rigs, 3) check("M17 vogne", round.plan.unloaders, 2) check("M17 stub", round.plan.mulch, true) check("M17 kalk fra", round.plan.lime, false) check("M17 jord", round.plan.soil, "plow") check("M17 så", round.plan.sow, true) check("M17 såafgrøde", round.plan.sowFruit, "WHEAT") check("M17 tromle", round.plan.roll, true) check("M17 gødning fra", round.plan.fertilize, false) check("M17 ukrudt fra", round.plan.weed, false) check("M17 art", round.kind, "harvest") check("M17 sidste afgrøde", round.lastFruit, "BARLEY") check("M17 pleje-tæller", round.careRuns.weeding, 1) check("M17 produkt", round.plan.product, "harvest") local legacy = M.fromRecord({id = 5, stage = "collecting", mode = "auto", product = "hay", rake = 1}) check("M17 etape 1-post: hø", legacy.plan.product, "hay") check("M17 etape 1-post: standard kalk til", legacy.plan.lime, true) check("M17 etape 1-post: 1 sæt", legacy.plan.rigs, 1) -- M18: Start nu på en bar mark (klar-andel 0) må ikke nulstilles af vækst-reglen før den starter local bare = M.with(M.forceReady(M.newField(70)), {startRequested = true}) check("M18 bar mark forbliver klar", M.onReadiness(bare, 0).stage, "ready") check("M18 uden forespørgsel -> venter", M.onReadiness(M.forceReady(M.newField(71)), 0).stage, "idle") check("M18 vejrfølsom: høst", M.isWeatherSensitive("harvesting", {}, "harvest"), true) check("M18 vejrfølsom: kultivering ikke", M.isWeatherSensitive("cultivating", {}, "harvest"), false) -- M19: mål-afgrøde — "vælg mark + afgrøde, glem den" local target = M.with(M.newField(80), {plan = {targetFruit = "WHEAT"}}) local wheatNow = M.applyTarget(target.plan, "harvest", "BARLEY") check("M19 mål -> auto", wheatNow.mode, "auto") check("M19 høst byg -> så hvede", wheatNow.sow, true) check("M19 såafgrøde = mål", wheatNow.sowFruit, "WHEAT") check("M19 kæde: høst -> kultivér -> så", table.concat(M.workStages(wheatNow, "harvest", {}), ","), "harvesting,cultivating,sowing") local grassToWheat = M.applyTarget(target.plan, "mowable", "GRASS") check("M19 græs -> hvede: slå, saml op, så klargør og så", table.concat(M.workStages(grassToWheat, "mowable", {}), ","), "mowing,collecting,cultivating,sowing") local grassTarget = M.applyTarget(M.with(M.newField(81), {plan = {targetFruit = "GRASS"}}).plan, "mowable", "GRASS") check("M19 græs -> græs: kun slå og saml op", table.concat(M.workStages(grassTarget, "mowable", {}), ","), "mowing,collecting") check("M19 græs -> græs: ingen såning", grassTarget.sow, false) local bareTarget = M.applyTarget(target.plan, "bare", nil) check("M19 bar mark -> klargør og så", table.concat(M.workStages(bareTarget, "bare", {plow = true}), ","), "plowing,sowing") check("M19 uden mål: plan urørt", M.applyTarget(M.newField(82).plan, "harvest", "BARLEY").mode, "manual") local started = M.start(target, "harvest", {}, "BARLEY") check("M19 start anvender målet", started.plan.sowFruit, "WHEAT") check("M19 start: høst først", started.stage, "harvesting") check("M19 målet gemmes", M.fromRecord(M.toRecord(started)).plan.targetFruit, "WHEAT") check("M19 klargøring efter slåning gemmes", M.fromRecord(M.toRecord(M.start(target, "mowable", {}, "GRASS"))).plan.prepAfterMow, true) -- M20: skal en bar mark med mål starte? (kun når målafgrøden kan sås nu) check("M20 bar + sås nu -> start", M.shouldStartBare(target, "bare", true), true) check("M20 bar + ikke såtid -> vent", M.shouldStartBare(target, "bare", false), false) check("M20 uden mål -> ingen auto-start", M.shouldStartBare(M.newField(83), "bare", true), false) check("M20 voksende mark -> nej", M.shouldStartBare(target, "harvest", true), false) check("M20 arbejdende mark -> nej", M.shouldStartBare(started, "bare", true), false) check("M20 færdig (lige sået) -> nej, ingen dobbelt-såning", M.shouldStartBare(M.with(target, {stage = "done"}), "bare", true), false) -- M21: vælg mål-afgrøde fra menuen: auto, og en færdig mark nulstilles så den kan starte local chosen = M.setTarget(M.with(M.newField(84), {stage = "done"}), "CANOLA") check("M21 mål sat", chosen.plan.targetFruit, "CANOLA"); check("M21 auto", chosen.plan.mode, "auto") check("M21 færdig -> venter", chosen.stage, "idle") check("M21 arbejdende mark beholder trin", M.setTarget(started, "CANOLA").stage, "harvesting") local cleared = M.setTarget(chosen, "") check("M21 mål fjernet", cleared.plan.targetFruit, ""); check("M21 tilbage til manuel", cleared.plan.mode, "manual") -- M22: halm efter høst — hakkes (standard), samles op, eller presses til baller og samles local strawBase = {soil = "none", sow = false, lime = false} check("M22 hakkes: kun høst", table.concat(M.workStages(strawBase, "harvest", {}), ","), "harvesting") check("M22 samles op", table.concat(M.workStages(M.with(M.newField(1), {plan = {soil = "none", lime = false, straw = "collect"}}).plan, "harvest", {}), ","), "harvesting,strawCollecting") check("M22 baller", table.concat(M.workStages(M.with(M.newField(1), {plan = {soil = "none", lime = false, straw = "bale"}}).plan, "harvest", {}), ","), "harvesting,baling,baleCollecting") local baleChain = M.start(M.with(M.newField(2), {plan = {straw = "bale", soil = "cultivate", lime = false}}), "harvest", {}) check("M22 høst -> presses", M.advance(baleChain, {}).stage, "baling") check("M22 presses -> baller samles", M.advance(M.advance(baleChain, {}), {}).stage, "baleCollecting") check("M22 -> kultivér", M.advance(M.advance(M.advance(baleChain, {}), {}), {}).stage, "cultivating") check("M22 roller", M.roleFor("baling") .. "," .. M.roleFor("baleCollecting") .. "," .. M.roleFor("strawCollecting"), "baler,baleCollector,collector") check("M22 halm-læssevogn følger høsterens kursus", M.courseSource(M.with(M.newField(3), {stage = "strawCollecting"}), "collector"), "harvester") check("M22 presser følger høsterens kursus", M.courseSource(M.with(M.newField(3), {stage = "baling"}), "baler"), "harvester") check("M22 græs-læssevogn følger stadig slåkurset", M.courseSource(M.with(M.newField(3), {stage = "collecting"}), "collector"), "mower") check("M22 halm-læssevogn uden breddekrav", M.requiredWidth(M.with(M.newField(3), {stage = "strawCollecting", mowWidth = 40}), "collector"), nil) check("M22 presning vejrfølsom", M.isWeatherSensitive("baling", {}, "harvest"), true) check("M22 ballesamling ikke vejrfølsom", M.isWeatherSensitive("baleCollecting", {}, "harvest"), false) check("M22 gemmes", M.fromRecord(M.toRecord(M.with(M.newField(4), {plan = {straw = "bale"}}))).plan.straw, "bale") check("M22 ukendt -> hakkes", M.fromRecord({id = 5, straw = "x"}).plan.straw, "chop") -- M23: ingen ny tabel når vækst-reglen ikke ændrer noget (undgår affald hvert sekund -> GC-hak) local calm = M.newField(95) check("M23 uændret -> samme tabel", M.onReadiness(calm, 0.2) == calm, true) check("M23 ændret -> ny tabel", M.onReadiness(calm, 0.95) ~= calm, true) -- M24: passive marker kan springes over (manuel, uden mål, venter, ingen forespørgsel) check("M24 manuel venter = passiv", M.isPassive(calm), true) check("M24 auto = ikke passiv", M.isPassive(M.with(calm, {plan = {mode = "auto"}})), false) check("M24 mål = ikke passiv", M.isPassive(M.setTarget(calm, "WHEAT")), false) check("M24 klar = ikke passiv", M.isPassive(M.with(calm, {stage = "ready"})), false) check("M24 i gang = ikke passiv", M.isPassive(started), false) check("M24 Start nu = ikke passiv", M.isPassive(M.with(calm, {startRequested = true})), false) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)