-- Kør: luajit tests/test_adFieldSilos.lua (fra repo-roden) dofile("FS25_ADSmartPickup/adFieldSiloPlanner.lua") dofile("FS25_ADSmartPickup/adFieldSiloConfig.lua") dofile("FS25_ADSmartPickup/adFieldSilos.lua") local S = ADFieldSilos 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 local loan = {startedMs = 0, lastProgressMs = 0, lastFill = 0} check("kører, tidligt -> behold", S.releaseReason(loan, {adActive = true, controlled = false, fill = 0, moving = true}, 5000), nil) check("AD slukket efter 40 s -> done", S.releaseReason(loan, {adActive = false, controlled = false, fill = 0}, 40000), "done") check("spiller i vognen -> player", S.releaseReason(loan, {adActive = true, controlled = true, fill = 0}, 5000), "player") check("står stille uden fyld i 120 s -> stuck", S.releaseReason(loan, {adActive = true, controlled = false, fill = 0, moving = false}, 120001), "stuck") check("kører stadig efter 120 s -> behold", S.releaseReason(loan, {adActive = true, controlled = false, fill = 0, moving = true}, 120001), nil) local loading = {startedMs = 0, lastProgressMs = 110000, lastFill = 0} check("fyldning steg for nylig -> behold", S.releaseReason(loading, {adActive = true, controlled = false, fill = 20000, moving = false}, 125000), nil) -- I2/I3: vagten gælder kun før turen er kommet i gang; AD der slukker straks uden fremdrift = fejl local going = {startedMs = 0, lastFill = 30000, hasProgressed = true} check("I2 i gang, står i kø 500 s -> behold", S.releaseReason(going, {adActive = true, controlled = false, fill = 30000, moving = false}, 500000), nil) check("I2 aflæsning (fyld falder) er fremdrift", S.releaseReason({startedMs = 0, lastFill = 30000}, {adActive = true, controlled = false, fill = 20000, moving = false}, 500000), nil) check("I3 AD slukket efter 10 s uden fremdrift -> failed", S.releaseReason({startedMs = 0, lastFill = 0}, {adActive = false, controlled = false, fill = 0}, 10000), "failed") check("I3 AD slukket efter 10 s med fremdrift -> done", S.releaseReason({startedMs = 0, lastFill = 0, hasProgressed = true}, {adActive = false, controlled = false, fill = 0}, 10000), "done") -- Røgtest af spil-laget med mocks: planNow låner nærmeste tømmevogn, isLoaned = optaget, -- frakobling (setConfig enabled=false) stopper AD og frigiver (Review Focus). do local WHEAT = 1 Logging = {info = function() end, warning = function() end} g_currentMission = {getFarmId = function() return 1 end} g_fillTypeManager = {getFillTypeByIndex = function() return {title = "Hvede"} end} function getWorldTranslation(node) return node.x, 0, node.z end ADRuns = {vehicleLabel = function(v) return v.name end} local function storage(levels, free) return {fillLevels = levels, fillTypes = {[WHEAT] = true}, getFreeCapacity = function() return free end} end local fieldSilo = {id = "fs", name = "Marksilo 1", kind = "silo", placeable = {rootNode = {x = 1000, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} local farm = {id = "farm", name = "Gårdsilo", kind = "silo", placeable = {rootNode = {x = 0, z = 0}, spec_silo = {storages = {storage({}, 500000)}}}} ADBuildings = {list = function() return {fieldSilo, farm} end, getMarkers = function(b, role) return {{id = b.id .. "-" .. role}} end} ADFields = {list = function() return {{id = 1}} end, fieldSilos = function() return {fieldSilo, farm} end} local started, settingsSet, noPark, noRoute, residue = {}, {}, {}, {}, 0 ADRunsController = {store = {runs = {}}, setSetting = function(_, v, name, value) settingsSet[v] = settingsSet[v] or {}; settingsSet[v][name] = value end} ADUnloadWait = {hasRouteTo = function(_, _, id) return not noRoute[id] end} local function makeVehicle(name, x) local module = {active = false} function module:setMode() end function module:setFirstMarkerByWayPointId(id) self.first = id end function module:setSecondMarkerByWayPointId(id) self.second = id end function module:setFillType() end function module:setLoopsDone() end function module:raiseDirtyFlag() end function module:isActive() return self.active end return {name = name, x = x, lastSpeedReal = 0, ad = {stateModule = module}, stopAutoDrive = function(self) self.ad.stateModule.active = false; self.stopped = true end} end local near, far = makeVehicle("Nær", 900), makeVehicle("Fjern", 5000) local adEnv = {AutoDrive = {MODE_PICKUPANDDELIVER = 2, RT_NONE = 0, getAllDischargeableUnits = function() return {{fillUnitIndex = 1, object = { getFillUnitCapacity = function() return 40000 end, getFillUnitSupportedFillTypes = function() return {[WHEAT] = true} end, getFillUnitFillLevel = function() return residue end, getFillUnitFillType = function() return WHEAT end}}} end, getAllUnits = function() return {} end, getAllFillLevels = function() return 0 end}} ADFieldWork = { getRigs = function() local rigs = {} -- som spillets vehicleSystem: slettede køretøjer er ikke med if not near.isDeleted then table.insert(rigs, {id = "n", vehicle = near, role = "unloader", enabled = true, busy = S.isLoaned(near), controlled = false, x = 900, z = 0}) end if not far.isDeleted then table.insert(rigs, {id = "f", vehicle = far, role = "unloader", enabled = true, busy = S.isLoaned(far), controlled = false, x = 5000, z = 0}) end table.insert(rigs, {id = "h", vehicle = far, role = "harvester", enabled = true, busy = false, controlled = false, x = 1000, z = 0}) return rigs end, enableParkAtJobFinished = function() end, parkMarkerId = function(_, v) if noPark[v] then return nil end return 777 end, startAd = function(_, v, _, first, second, fillType) v.ad.stateModule.active = true; table.insert(started, {v = v, first = first, second = second, fillType = fillType}) end, } S.setConfig(ADFieldSiloConfig.with(ADFieldSiloConfig.toggleStore(ADFieldSiloConfig.new(), "farm"), {enabled = true, maxVehicles = 1})) local plan = S.planNow(adEnv) check("RØG én afsendelse (x=1)", #started, 1) check("RØG nærmeste tømmevogn", started[1] ~= nil and started[1].v.name, "Nær") check("RØG fra marksilo-markør", started[1] ~= nil and started[1].first, "fs-load") check("RØG til gårdlager-markør", started[1] ~= nil and started[1].second, "farm-unload") check("RØG varen med", started[1] ~= nil and started[1].fillType, WHEAT) check("RØG gårdlager er ikke marksilo", #plan.status, 1) check("RØG vognen er lånt", S.isLoaned(near), true) check("RØG x nået -> ingen flere", #S.planNow(adEnv).dispatch, 0) check("I4 rotation slået fra på lånt vogn", settingsSet[near] ~= nil and settingsSet[near].rotateTargets, 0) S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) check("RØG frakobling stopper AD", near.stopped, true) check("RØG frakobling frigiver", S.isLoaned(near), false) -- I5: vogn uden parkeringspunkt lånes ikke (ville blive holdende på lagerets markør) started = {} noPark[near] = true near.stopped = nil S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = true})) S.planNow(adEnv) check("I5 uden parkering -> fjern vogn med parkering", started[1] ~= nil and started[1].v.name, "Fjern") S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) noPark[near] = nil -- I3: ingen rute til siloen -> ingen afsendelse started = {} noRoute["fs-load"] = true S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = true})) S.planNow(adEnv) check("I3 ingen rute -> ingen afsendelse", #started, 0) noRoute["fs-load"] = nil S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) -- I6: silo der er kilde/mål i et flow er ikke marksilo ADRunsController.store.runs = {{kind = "supply", sourceBuildingIds = {}, targetBuildingIds = {"fs"}, storeBuildingIds = {}}} local silos = S.collectBuildings(adEnv) check("I6 silo i et flow tømmes ikke", #silos, 0) ADRunsController.store.runs = {} -- in-game 27/9: marksilo-flowet flyttede sukkerroer fra en Farma-silo til nabo-Farma-siloen (samme lagerklynge) local neighbour = {id = "nb", name = "Nabo-Farma", kind = "silo", placeable = {rootNode = {x = 40, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} ADFields.fieldSilos = function() return {fieldSilo, farm, neighbour} end local withNeighbour = S.collectBuildings(adEnv) local names = {} for _, silo in ipairs(withNeighbour) do table.insert(names, silo.id) end check("gårdens nabosilo (40 m fra gårdlager) er ikke marksilo", table.concat(names, ","), "fs") ADFields.fieldSilos = function() return {fieldSilo, farm} end -- in-game 27/9: gårdens Farma-klynge er 300 m lang — siloer over 100 m fra et valgt gårdlager blev marksiloer. -- En silo i samme klynge (≤ 60 m til en nabo) som et gårdlager er gårdens. local c1 = {id = "c1", name = "Klynge 1", kind = "silo", placeable = {rootNode = {x = 50, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} local c2 = {id = "c2", name = "Klynge 2", kind = "silo", placeable = {rootNode = {x = 105, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} ADBuildings.list = function() return {fieldSilo, farm, c1, c2} end ADFields.fieldSilos = function() return {fieldSilo, farm, c1, c2} end local clustered = {} for _, silo in ipairs(S.collectBuildings(adEnv)) do table.insert(clustered, silo.id) end check("gårdens klynge (105 m via nabo) er ikke marksilo", table.concat(clustered, ","), "fs") ADBuildings.list = function() return {fieldSilo, farm} end ADFields.fieldSilos = function() return {fieldSilo, farm} end -- M5: rest under 100 l tæller ikke som last residue = 50 local vehicles = S.collectVehicles(adEnv) check("M5 rest på 50 l er ikke last", vehicles[1].cargo, nil) residue = 0 -- M4: slettet lånt vogn fjernes uden at vælte tick S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = true})) started = {} S.planNow(adEnv) local loaned = started[1] ~= nil and started[1].v or nil if loaned ~= nil then loaned.isDeleted = true end local okTick = pcall(S.tick, adEnv, 2000) check("M4 tick tåler slettet vogn", okTick, true) check("M4 slettet vogn frigivet", loaned ~= nil and S.isLoaned(loaned), false) S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) end print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)