-- Kør: luajit tests/test_adRuns.lua (fra repo-roden) -- Tester run-modellen (rene funktioner) og XML-laget mod en XMLFile-mock. dofile("FS25_ADSmartPickup/adRuns.lua") 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 -- R1: opret run -> løbenummer, standardværdier; input-store røres ikke local empty = ADRuns.newStore() local s1, cow = ADRuns.createRun(empty, "Cow Feed") check("R1 id 1", cow.id, 1) check("R1 navn", cow.name, "Cow Feed") check("R1 auto-varer som standard", cow.ingredientMode, "auto") check("R1 loops 0", cow.loops, 0) check("R1 tom Wait-pulje", cow.waitPoolGroup, "") check("R1 input-store urørt", #empty.runs, 0) check("R1 nextId tæller op", s1.nextId, 2) local s2, pig = ADRuns.createRun(s1, "Pig Feed") check("R1 andet run får id 2", pig.id, 2) check("R1 to runs", #s2.runs, 2) -- R2: opdatér felter; ukendt mode ignoreres; loops klippes local s3 = ADRuns.updateRun(s2, 1, {name = "Cow 1", targetWayPointId = 94, ingredientMode = "manual", ingredients = {"STRAW", "FORAGE"}, waitPoolGroup = "Cow Wait", loops = 150}) local run1 = ADRuns.getRun(s3, 1) check("R2 navn", run1.name, "Cow 1") check("R2 mål", run1.targetWayPointId, 94) check("R2 manual", run1.ingredientMode, "manual") check("R2 varer", table.concat(run1.ingredients, ","), "STRAW,FORAGE") check("R2 pulje", run1.waitPoolGroup, "Cow Wait") check("R2 loops klippet til 99", run1.loops, 99) check("R2 gammel store urørt", ADRuns.getRun(s2, 1).name, "Cow Feed") local s3b = ADRuns.updateRun(s3, 1, {ingredientMode = "bogus", loops = -5}) check("R2 ukendt mode ignoreres", ADRuns.getRun(s3b, 1).ingredientMode, "manual") check("R2 loops under 0 -> 0", ADRuns.getRun(s3b, 1).loops, 0) check("R2 ukendt run -> uændret", #ADRuns.updateRun(s3, 99, {name = "x"}).runs, 2) check("R2 targetWayPointId=false rydder målet", ADRuns.getRun(ADRuns.updateRun(s3, 1, {targetWayPointId = false}), 1).targetWayPointId, nil) check("R2 nil rører ikke målet", ADRuns.getRun(ADRuns.updateRun(s3, 1, {name = "y"}), 1).targetWayPointId, 94) -- R3: tildel traktor; samme traktor flyttes mellem runs local s4, prev = ADRuns.assignVehicle(s3, 1, "vehicleA") check("R3 tildelt", ADRuns.getRun(s4, 1).vehicleIds[1], "vehicleA") check("R3 ingen tidligere", prev, nil) check("R3 opslag", ADRuns.findRunByVehicle(s4, "vehicleA").id, 1) local s5, prev2 = ADRuns.assignVehicle(s4, 2, "vehicleA") check("R3 flyttet fra run 1", prev2, 1) check("R3 væk fra run 1", #ADRuns.getRun(s5, 1).vehicleIds, 0) check("R3 i run 2", ADRuns.getRun(s5, 2).vehicleIds[1], "vehicleA") local s5b, prev3 = ADRuns.assignVehicle(s5, 2, "vehicleA") check("R3 gen-tildeling samme run -> ingen dublet", #ADRuns.getRun(s5b, 2).vehicleIds, 1) check("R3 gen-tildeling -> ingen 'flyttet fra'", prev3, nil) local s6 = ADRuns.unassignVehicle(s5, "vehicleA") check("R3 fjernet", ADRuns.findRunByVehicle(s6, "vehicleA"), nil) check("R3 ukendt run ved tildeling -> uændret", ADRuns.findRunByVehicle(ADRuns.assignVehicle(s6, 42, "vehicleB"), "vehicleB"), nil) -- R4: slet run local s7 = ADRuns.deleteRun(s5, 1) check("R4 ét run tilbage", #s7.runs, 1) check("R4 det rigtige", s7.runs[1].id, 2) check("R4 nextId bevares", s7.nextId, 3) -- R5: records frem og tilbage local records = ADRuns.toRecords(s5) check("R5 target som tal", records[1].target, 94) check("R5 varer som streng", records[1].ingredients, "STRAW FORAGE") check("R5 køretøjer", records[2].vehicleIds[1], "vehicleA") local back = ADRuns.fromRecords(records) check("R5 to runs tilbage", #back.runs, 2) check("R5 nextId = højeste + 1", back.nextId, 3) check("R5 varer parset", ADRuns.getRun(back, 1).ingredients[2], "FORAGE") check("R5 pulje", ADRuns.getRun(back, 1).waitPoolGroup, "Cow Wait") check("R5 target 0 -> nil", ADRuns.getRun(back, 2).targetWayPointId, nil) check("R5 køretøj", ADRuns.findRunByVehicle(back, "vehicleA").id, 2) -- R6: defekte poster springes over; dubletter af id ignoreres local dirty = ADRuns.fromRecords({ {id = "x", name = "Broken"}, {id = 5, name = "", }, {id = 7, name = "Ok", target = "12", ingredientMode = "weird", ingredients = "", waitPool = nil, loops = "3", vehicleIds = {}}, {id = 7, name = "Dublet"}, }) check("R6 kun den gyldige", #dirty.runs, 1) check("R6 target parses fra streng", dirty.runs[1].targetWayPointId, 12) check("R6 ukendt mode -> auto", dirty.runs[1].ingredientMode, "auto") check("R6 tom varestreng -> tom liste", #dirty.runs[1].ingredients, 0) check("R6 manglende pulje -> tom streng", dirty.runs[1].waitPoolGroup, "") check("R6 loops fra streng", dirty.runs[1].loops, 3) check("R6 nextId", dirty.nextId, 8) check("R6 tom liste", #ADRuns.fromRecords(nil).runs, 0) check("R6 parseIngredients trimmer", #ADRuns.parseIngredients(" STRAW FORAGE "), 2) -- R7: "kør hjem under X %" — standard 50, klippes til 10..90, overlever records local r7, r7run = ADRuns.createRun(ADRuns.newStore(), "Tærskel") check("R7 standard 50 %", r7run.returnBelowPercent, 50) check("R7 sættes", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 30}), r7run.id).returnBelowPercent, 30) check("R7 klippes til 90", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 150}), r7run.id).returnBelowPercent, 90) check("R7 klippes til 10", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 0}), r7run.id).returnBelowPercent, 10) local r7rec = ADRuns.toRecords(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 25})) check("R7 i records", r7rec[1].returnBelow, 25) check("R7 tilbage fra records", ADRuns.fromRecords(r7rec).runs[1].returnBelowPercent, 25) check("R7 manglende i gammel fil -> 50", ADRuns.fromRecords({{id = 1, name = "Gammel"}}).runs[1].returnBelowPercent, 50) -- R8: flere mål pr. run — liste + første som "primært" mål (bagudkompatibelt); toggle; records local r8, r8run = ADRuns.createRun(ADRuns.newStore(), "Kvæg") check("R8 ingen mål", #r8run.targetWayPointIds, 0) r8 = ADRuns.updateRun(r8, r8run.id, {targetWayPointIds = {94, 99}}) check("R8 to mål", table.concat(ADRuns.getRun(r8, r8run.id).targetWayPointIds, ","), "94,99") check("R8 primært mål = første", ADRuns.getRun(r8, r8run.id).targetWayPointId, 94) r8 = ADRuns.toggleTarget(r8, r8run.id, 94) check("R8 toggle fjerner", table.concat(ADRuns.getRun(r8, r8run.id).targetWayPointIds, ","), "99") check("R8 primært følger med", ADRuns.getRun(r8, r8run.id).targetWayPointId, 99) r8 = ADRuns.toggleTarget(r8, r8run.id, 120) check("R8 toggle tilføjer", table.concat(ADRuns.getRun(r8, r8run.id).targetWayPointIds, ","), "99,120") check("R8 gammel enkelt-sætning = liste med ét", table.concat(ADRuns.getRun(ADRuns.updateRun(r8, r8run.id, {targetWayPointId = 7}), r8run.id).targetWayPointIds, ","), "7") check("R8 false rydder listen", #ADRuns.getRun(ADRuns.updateRun(r8, r8run.id, {targetWayPointId = false}), r8run.id).targetWayPointIds, 0) local r8rec = ADRuns.toRecords(r8) check("R8 records: targets-streng", r8rec[1].targets, "99 120") check("R8 records: target = første (gammelt felt)", r8rec[1].target, 99) check("R8 tilbage fra records", table.concat(ADRuns.fromRecords(r8rec).runs[1].targetWayPointIds, ","), "99,120") check("R8 gammel fil med kun target", table.concat(ADRuns.fromRecords({{id = 1, name = "Gl", target = 55}}).runs[1].targetWayPointIds, ","), "55") -- ==== XML-laget mod en flad XMLFile-mock (nøgle -> værdi) ==== dofile("FS25_ADSmartPickup/adRunsStorage.lua") local function makeXmlMock(values) local xml = {values = values or {}, saved = false, deleted = false} xml.setInt = function(self, key, value) self.values[key] = value end xml.setString = function(self, key, value) self.values[key] = value end xml.getInt = function(self, key) return tonumber(self.values[key]) end xml.getString = function(self, key) local v = self.values[key]; return v ~= nil and tostring(v) or nil end -- iterate: kalder callback for "prefix(0)", "prefix(1)", ... så længe en nøgle med den stamme findes xml.iterate = function(self, prefix, callback) local index = 0 while true do local key = string.format("%s(%d)", prefix, index) local found = false for k in pairs(self.values) do if k:sub(1, #key) == key then found = true break end end if not found then return end callback(index + 1, key) index = index + 1 end end xml.save = function(self) self.saved = true end xml.delete = function(self) self.deleted = true end return xml end -- X1: records skrives som attributter + vehicle-underelementer local out = makeXmlMock() ADRunsStorage.writeRecords(out, ADRuns.toRecords(s5)) check("X1 version", out.values["ADSmartPickupRuns#version"], 1) check("X1 run 0 navn", out.values["ADSmartPickupRuns.run(0)#name"], "Cow 1") check("X1 run 0 target", out.values["ADSmartPickupRuns.run(0)#target"], 94) check("X1 run 0 varer", out.values["ADSmartPickupRuns.run(0)#ingredients"], "STRAW FORAGE") check("X1 run 0 pulje", out.values["ADSmartPickupRuns.run(0)#waitPool"], "Cow Wait") check("X1 run 1 køretøj", out.values["ADSmartPickupRuns.run(1).vehicle(0)#id"], "vehicleA") -- X2: samme data læses tilbage local records2 = ADRunsStorage.readRecords(makeXmlMock(out.values)) check("X2 to poster", #records2, 2) check("X2 id", records2[1].id, 1) check("X2 køretøj", records2[2].vehicleIds[1], "vehicleA") check("X2 loops", records2[1].loops, 99) check("X2 round-trip", ADRuns.findRunByVehicle(ADRuns.fromRecords(records2), "vehicleA").id, 2) -- X3: save/load via XMLFile-globalen; ingen savegame-mappe -> tom store, intet skrives Logging = Logging or {warning = function() end, info = function() end} local created, loaded = nil, nil XMLFile = { create = function(_, path, root) created = makeXmlMock(); created.path = path; created.root = root; return created end, load = function(_, path) loaded = makeXmlMock(created and created.values or {}); loaded.path = path; return loaded end, } fileExists = function(path) return created ~= nil and created.path == path end g_currentMission = {missionInfo = {savegameDirectory = "/tmp/sg1"}} check("X3 gemt", ADRunsStorage.save(s5), true) check("X3 sti", created.path, "/tmp/sg1/ADSmartPickup_runs.xml") check("X3 rodelement", created.root, "ADSmartPickupRuns") check("X3 save+delete kaldt", created.saved and created.deleted, true) check("X3 indlæst", #ADRunsStorage.load().runs, 2) check("X3 læse-handle lukket", loaded.deleted, true) g_currentMission = {missionInfo = {}} check("X3 uden savegame -> false", ADRunsStorage.save(s5), false) check("X3 uden savegame -> tom store", #ADRunsStorage.load().runs, 0) g_currentMission = {missionInfo = {savegameDirectory = "/tmp/sg-ny"}} check("X3 fil mangler -> tom store", #ADRunsStorage.load().runs, 0) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)