feat(lagre): vareoversigt med nøgletal, 'kræver handling' og fyldningsbjælker — klik på en vare folder den ud til bygningerne; bygningstabellen bag 'Vis: Varer | Bygninger' (v1.52.0.0)
This commit is contained in:
@@ -76,5 +76,111 @@ check("ST3 paller med", egg.count, 24)
|
||||
check("ST3 pladser med", egg.slots, 4000)
|
||||
check("ST3 procent af pladser", egg.percent, 1)
|
||||
|
||||
|
||||
-- ---- v1.52 vareoversigt ----
|
||||
|
||||
-- SV1: gruppér fanens bygninger pr. vare; bygninger sorteret mest først, varer efter titel
|
||||
local titles = {[31] = "Straw", [5] = "Sugarbeet", [9] = "Milk"}
|
||||
local titleOf = function(ft) return titles[ft] end
|
||||
local siloInv = {
|
||||
{name = "Farma #1", placeable = "p1", entries = {{fillType = 31, level = 300, capacity = 1000}, {fillType = 5, level = 50, capacity = 100}}},
|
||||
{name = "Farma #2", placeable = "p2", entries = {{fillType = 31, level = 700, capacity = 1000}}},
|
||||
{name = "Tom", placeable = "p3", entries = {}},
|
||||
}
|
||||
local groups = S.groupByFillType(siloInv, titleOf)
|
||||
check("SV1 to varer", #groups, 2)
|
||||
check("SV1 alfabetisk: Straw først", groups[1].title, "Straw")
|
||||
check("SV1 straw total", groups[1].level, 1000)
|
||||
check("SV1 straw kapacitet", groups[1].capacity, 2000)
|
||||
check("SV1 straw procent", groups[1].percent, 50)
|
||||
check("SV1 to siloer med straw", #groups[1].buildings, 2)
|
||||
check("SV1 flest først", groups[1].buildings[1].name, "Farma #2")
|
||||
check("SV1 bygning procent", groups[1].buildings[1].percent, 70)
|
||||
check("SV1 placeable med", groups[1].buildings[2].placeable, "p1")
|
||||
check("SV1 tomt inventar", #S.groupByFillType({}, titleOf), 0)
|
||||
|
||||
-- SV2: gårdens kapacitet pr. vare
|
||||
local caps = S.farmCapacities(siloInv)
|
||||
check("SV2 kapacitet straw", caps[31], 2000)
|
||||
|
||||
-- SV3: varens statistik: døgn til tom på gårdens netto, pris i % af top
|
||||
local straw = S.groupStats(groups[1], {level = 1500, capacity = 3000}, {consumption = 1000, production = 0}, {price = 0.5, top = 1.0})
|
||||
check("SV3 døgn til tom", straw.days, 1.5)
|
||||
check("SV3 døgn til fuld nil (falder)", straw.daysToFull, nil)
|
||||
check("SV3 værdi nu", straw.valueNow, 500)
|
||||
check("SV3 pris % af top", straw.priceRatio, 0.5)
|
||||
check("SV3 tomt-niveau gul (< 3 døgn)", straw.emptyLevel, "yellow")
|
||||
check("SV3 samlet niveau", straw.severity, "yellow")
|
||||
|
||||
-- SV4: produceret vare -> døgn til fuld på gårdens ledige kapacitet
|
||||
local sugar = S.groupStats({fillType = 5, title = "Sugar", level = 950, capacity = 1000, percent = 95, buildings = {}},
|
||||
{level = 950, capacity = 1000}, {consumption = 0, production = 100}, nil)
|
||||
check("SV4 døgn til fuld", sugar.daysToFull, 0.5)
|
||||
check("SV4 producerer", sugar.producing, true)
|
||||
check("SV4 fuld-niveau rød (95 %)", sugar.fullLevel, "red")
|
||||
check("SV4 ingen pris -> nil", sugar.valueNow, nil)
|
||||
|
||||
-- SV5: fuld lagervare der ikke produceres er ikke et problem
|
||||
local stock = S.groupStats({fillType = 31, title = "Straw", level = 990, capacity = 1000, percent = 99, buildings = {}}, nil, nil, nil)
|
||||
check("SV5 fuld lager uden produktion -> intet", stock.fullLevel, nil)
|
||||
check("SV5 severity nil", stock.severity, nil)
|
||||
|
||||
-- SV6: tærskler
|
||||
check("SV6 < 1 døgn rød", S.emptyLevel(0.5), "red")
|
||||
check("SV6 < 3 døgn gul", S.emptyLevel(2.9), "yellow")
|
||||
check("SV6 5 døgn intet", S.emptyLevel(5), nil)
|
||||
check("SV6 nil intet", S.emptyLevel(nil), nil)
|
||||
check("SV6 fuld 85 % gul", S.fullLevel(85, true), "yellow")
|
||||
check("SV6 fuld 84 % intet", S.fullLevel(84, true), nil)
|
||||
|
||||
-- SV7: bygningsniveau: stald-foder næsten tomt, fabrik-output næsten fuldt
|
||||
check("SV7 stald 8 % rød", S.buildingLevel({percent = 8}, "husbandry", false), "red")
|
||||
check("SV7 stald 20 % gul", S.buildingLevel({percent = 20}, "husbandry", false), "yellow")
|
||||
check("SV7 stald-output (mælk) 8 % intet", S.buildingLevel({percent = 8}, "husbandry", true), nil)
|
||||
check("SV7 output 96 % rød", S.buildingLevel({percent = 96, isOutput = true}, "factory", false), "red")
|
||||
check("SV7 silo 50 % intet", S.buildingLevel({percent = 50}, "silo", false), nil)
|
||||
|
||||
-- SV8: kræver handling — rød tom > rød fuld > gul tom > gul fuld > sælg (efter kroner); højst max
|
||||
local mk = function(t) t.buildings = {}; return t end
|
||||
local list = S.attentionList({
|
||||
mk({title = "A", emptyLevel = "yellow", days = 2, consumption = 1}),
|
||||
mk({title = "B", fullLevel = "red", percent = 97, producing = true}),
|
||||
mk({title = "C", emptyLevel = "red", days = 0.4, consumption = 1}),
|
||||
mk({title = "D", priceRatio = 0.95, valueNow = 50000, net = 0}),
|
||||
mk({title = "E", priceRatio = 0.97, valueNow = 900000, net = 0}),
|
||||
mk({title = "F", priceRatio = 0.99, valueNow = 900000, net = -5}), -- gården æder den: sælg ikke
|
||||
mk({title = "G", priceRatio = 0.99, valueNow = 5000, net = 0}), -- for lille
|
||||
}, 4)
|
||||
check("SV8 højst 4", #list, 4)
|
||||
check("SV8 1: rød tom", list[1].stat.title, "C")
|
||||
check("SV8 1: art", list[1].kind, "empty")
|
||||
check("SV8 2: rød fuld", list[2].stat.title, "B")
|
||||
check("SV8 3: gul tom", list[3].stat.title, "A")
|
||||
check("SV8 4: største salg", list[4].stat.title, "E")
|
||||
check("SV8 4: art", list[4].kind, "sell")
|
||||
|
||||
-- SV9: nøgletal for fanen
|
||||
local kpi = S.kpis({
|
||||
mk({valueNow = 100, valueTop = 150, emptyLevel = "red"}),
|
||||
mk({valueNow = 50, valueTop = 50, fullLevel = "yellow"}),
|
||||
mk({}),
|
||||
})
|
||||
check("SV9 værdi nu", kpi.valueNow, 150)
|
||||
check("SV9 ekstra ved top", kpi.gain, 50)
|
||||
check("SV9 løber tør", kpi.emptyCount, 1)
|
||||
check("SV9 fyldt", kpi.fullCount, 1)
|
||||
|
||||
-- SV10: fyldningsbrøk til bjælker, klemt til [0,1]
|
||||
check("SV10 halv", S.fraction(50), 0.5)
|
||||
check("SV10 over 100", S.fraction(130), 1)
|
||||
check("SV10 nil", S.fraction(nil), 0)
|
||||
|
||||
-- SV11: skur uden pladsgrænse (kapacitet 0) -> varens procent ukendt, ikke 940 %
|
||||
local shedGroups = S.groupByFillType({
|
||||
{name = "A", entries = {{fillType = 9, level = 40, capacity = 100}}},
|
||||
{name = "B", entries = {{fillType = 9, level = 900, capacity = 0}}},
|
||||
}, titleOf)
|
||||
check("SV11 procent nil ved ubegrænset skur", shedGroups[1].percent, nil)
|
||||
|
||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||
os.exit(failures == 0 and 0 or 1)
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
-- Kør: luajit tests/test_storageView.lua (fra repo-roden)
|
||||
-- Røgtest af vareoversigten i fanerne Lagre (v1.52) med falske GUI-elementer: opbygning, fold ud,
|
||||
-- opdatering hvert sekund og skift til bygningsvisning må ikke kaste, og tallene skal lande i cellerne.
|
||||
|
||||
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
|
||||
|
||||
-- ---- falske GUI-elementer ----
|
||||
local Element = {}
|
||||
Element.__index = Element
|
||||
local function newElement(names, size)
|
||||
local element = setmetatable({children = {}, text = nil, visible = true, size = size or {0.5, 0.02}}, Element)
|
||||
for _, name in ipairs(names or {}) do
|
||||
element.children[name] = setmetatable({children = {}, name = name, visible = true, size = {0.2, 0.01}}, Element)
|
||||
end
|
||||
return element
|
||||
end
|
||||
function Element:clone() local copy = newElement({}, {self.size[1], self.size[2]}); for n, c in pairs(self.children) do copy.children[n] = setmetatable({children = {}, name = n, visible = true, size = {c.size[1], c.size[2]}}, Element) end; return copy end
|
||||
function Element:getDescendantByName(name) return self.children[name] end
|
||||
function Element:setText(text) self.text = text end
|
||||
function Element:setTexts(texts) self.texts = texts end
|
||||
function Element:setState(state) self.state = state end
|
||||
function Element:setVisible(visible) self.visible = visible end
|
||||
function Element:setSize(x, y) self.size = {x, y} end
|
||||
function Element:setImageColor(_, r, g, b, a) self.color = {r, g, b, a} end
|
||||
function Element:setTextColor(r, g, b, a) self.textColor = {r, g, b, a} end
|
||||
function Element:delete() end
|
||||
function Element:mouseEvent() return false end
|
||||
|
||||
-- ---- spil-globaler ----
|
||||
Class = function() return {} end
|
||||
TabbedMenuFrameElement = {}
|
||||
InputAction = {}
|
||||
FocusManager = {loadElementFromCustomValues = function() end, removeElement = function() end}
|
||||
Logging = {warning = function(...) print("WARN", string.format(...)) end, info = function() end}
|
||||
local titles = {[31] = "Straw", [5] = "Sugar", [9] = "Milk"}
|
||||
g_fillTypeManager = {getFillTypeTitleByIndex = function(_, i) return titles[i] end, getFillTypeNameByIndex = function(_, i) return titles[i] end}
|
||||
g_currentMission = {getFarmId = function() return 1 end}
|
||||
g_i18n = nil
|
||||
|
||||
dofile("FS25_ADSmartPickup/adStorageStats.lua")
|
||||
ADRunsController = {store = {runs = {}}}
|
||||
ADRuns = {getRun = function() return nil end}
|
||||
ADFlowView = {wrapText = function(text) return {text} end}
|
||||
ADSources = {
|
||||
getFarmInventory = function() return ADSources.inventory end,
|
||||
getFarmRates = function() return {[31] = {consumption = 1000, production = 0}, [5] = {consumption = 0, production = 500}} end,
|
||||
getMarketQuote = function(ft) if ft == 5 then return {price = 1.0, top = 1.05} end return nil end,
|
||||
}
|
||||
ADSources.inventory = {
|
||||
{kind = "silo", name = "Farma #1", placeable = "p1", entries = {{fillType = 31, level = 300, capacity = 1000}, {fillType = 5, level = 20000, capacity = 21000}}},
|
||||
{kind = "silo", name = "Farma #2", placeable = "p2", entries = {{fillType = 31, level = 700, capacity = 1000}}},
|
||||
{kind = "factory", name = "Sugar mill", placeable = "p3", entries = {{fillType = 5, level = 10, capacity = 100, isOutput = true}}},
|
||||
}
|
||||
|
||||
local i18nTexts = {}
|
||||
local i18n = {getText = function(_, key) i18nTexts[key] = true; if key:find("days") then return "%.1f d" end
|
||||
if key:find("share") then return "%d %%" end
|
||||
if key:find("alert_full") then return "%s %d %% %s" end
|
||||
if key:find("alert_empty") then return "%s %s %s %s" end
|
||||
if key:find("alert_sell") then return "%s %d %s" end
|
||||
if key:find("pallets") then return "%d p" end
|
||||
if key:find("perDay") or key:find("fullIn") then return "%s" end
|
||||
return key end}
|
||||
|
||||
SmartPickupFrame = {}
|
||||
local function load(path) local chunk = assert(loadfile(path)); chunk() end
|
||||
SmartPickupFrame.new = nil
|
||||
load("FS25_ADSmartPickup/gui/SmartPickupFrame.lua")
|
||||
load("FS25_ADSmartPickup/gui/SmartPickupStorageTab.lua")
|
||||
load("FS25_ADSmartPickup/gui/SmartPickupStorageView.lua")
|
||||
load("FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua")
|
||||
|
||||
-- en frame uden spillets klasse-maskineri: kun det fanen bruger
|
||||
local frame = setmetatable({}, {__index = SmartPickupFrame})
|
||||
frame.i18n = i18n
|
||||
frame.clonedRows, frame.rowData = {}, {}
|
||||
frame.currentTab = 7 -- Siloer
|
||||
local rows = {}
|
||||
frame.boxLayout = {elements = rows, invalidateLayout = function() end}
|
||||
frame.productPrefab = newElement({"option", "title"})
|
||||
frame.productPrefab.children.option.children.ignore = newElement()
|
||||
frame.textPrefab = newElement({"title"})
|
||||
frame.kpiPrefab = newElement({"v1", "l1", "v2", "l2", "v3", "l3", "v4", "l4"})
|
||||
frame.alertPrefab = newElement({"dot", "c1"})
|
||||
frame.itemPrefab = newElement({"c1", "c2", "c3", "c4", "bar", "barBg"})
|
||||
frame.subPrefab = newElement({"c1", "c2", "c3", "c4", "bar", "barBg"})
|
||||
frame.statPrefab = newElement({"c1", "c2", "c3", "c4", "c5", "c6", "c7"})
|
||||
frame.gridPrefab = newElement({"c1", "c2", "c3"})
|
||||
for _, prefab in ipairs({frame.productPrefab, frame.textPrefab, frame.kpiPrefab, frame.alertPrefab, frame.itemPrefab, frame.subPrefab, frame.statPrefab, frame.gridPrefab}) do
|
||||
local base = prefab.clone
|
||||
prefab.clone = function(self) local copy = base(self); if self.children.option then copy.children.option.children.ignore = newElement() end; table.insert(rows, copy); return copy end
|
||||
end
|
||||
frame.getAdEnv = function() return {} end
|
||||
frame.setStatusText = function() end
|
||||
frame.refreshButtons = function() end
|
||||
frame.restoreFocusIfLost = function() end
|
||||
frame.colorRows = function() end
|
||||
for _, name in ipairs({"startButtonInfo", "renameButtonInfo", "deleteButtonInfo", "stopAllButtonInfo", "renameBuildingButtonInfo"}) do frame[name] = {} end
|
||||
|
||||
local function texts()
|
||||
local list = {}
|
||||
for _, row in ipairs(rows) do
|
||||
for _, name in ipairs({"title", "c1", "c2", "c3", "c4", "v1", "v3"}) do
|
||||
local child = row.children[name]
|
||||
if child ~= nil and child.text ~= nil then table.insert(list, child.text) end
|
||||
end
|
||||
end
|
||||
return table.concat(list, "|")
|
||||
end
|
||||
|
||||
-- SVW1: opbygning af Siloer i varevisning
|
||||
local ok, err = pcall(frame.rebuild, frame)
|
||||
check("SVW1 opbygning kaster ikke", ok, true)
|
||||
if not ok then print(err) end
|
||||
local all = texts()
|
||||
check("SVW1 straw-række", all:find("+ Straw", 1, true) ~= nil, true)
|
||||
check("SVW1 sugar-række", all:find("+ Sugar", 1, true) ~= nil, true)
|
||||
check("SVW1 straw 1.0k/2.0k 50 %", all:find("1.0k/2.0k · 50 %", 1, true) ~= nil, true)
|
||||
check("SVW1 ingen bygninger før klik", all:find("Farma #2", 1, true), nil)
|
||||
check("SVW1 fabrikkens sukker ikke i siloer", all:find("Sugar mill", 1, true), nil)
|
||||
check("SVW1 kræver handling: straw løber tør", all:find("spu_alert", 1, true) == nil and all:find("Straw 1.0 d", 1, true) ~= nil, true)
|
||||
|
||||
-- SVW2: klik på straw folder ud; bygninger med mest først
|
||||
local strawRow = frame.itemView.itemRows[1]
|
||||
ok, err = pcall(frame.toggleItem, frame, 31)
|
||||
check("SVW2 fold ud kaster ikke", ok, true)
|
||||
if not ok then print(err) end
|
||||
all = texts()
|
||||
local first, second = all:find("Farma #2", 1, true), all:find("Farma #1", 1, true)
|
||||
check("SVW2 Farma #2 vist", first ~= nil, true)
|
||||
check("SVW2 flest først", first ~= nil and second ~= nil and first < second, true)
|
||||
check("SVW2 markør skiftet", all:find("– Straw", 1, true) ~= nil, true)
|
||||
check("SVW2 andel", all:find("70 %", 1, true) ~= nil, true)
|
||||
|
||||
-- SVW3: opdatering hvert sekund med nye tal, samme struktur
|
||||
ADSources.inventory[2].entries[1].level = 900
|
||||
ok, err = pcall(frame.refreshLive, frame)
|
||||
check("SVW3 opdatering kaster ikke", ok, true)
|
||||
if not ok then print(err) end
|
||||
check("SVW3 ny total i cellen", texts():find("1.2k/2.0k · 60 %", 1, true) ~= nil, true)
|
||||
local bar = frame.itemView.itemRows[1].bar
|
||||
check("SVW3 bjælke 60 %", math.floor(bar.size[1] / 0.2 * 100 + 0.5), 60)
|
||||
check("SVW3 bjælke gul (løber tør < 3 d)", bar.color ~= nil and bar.color[1] > 0.9 and bar.color[2] > 0.7, true)
|
||||
|
||||
-- SVW4: skift til bygningsvisning og tilbage
|
||||
ok, err = pcall(frame.onClickStorageView, frame, SmartPickupFrame.STORAGE_VIEW_BUILDINGS)
|
||||
check("SVW4 bygningsvisning kaster ikke", ok, true)
|
||||
if not ok then print(err) end
|
||||
check("SVW4 ingen varevisning", frame.itemView, nil)
|
||||
ok = pcall(frame.onClickStorageView, frame, SmartPickupFrame.STORAGE_VIEW_ITEMS)
|
||||
check("SVW4 tilbage til varer", ok and frame.itemView ~= nil, true)
|
||||
|
||||
-- SVW5: fabrikker-fanen: output næsten fuldt -> rød
|
||||
frame.currentTab = 6
|
||||
ok, err = pcall(frame.rebuild, frame)
|
||||
check("SVW5 fabrikker kaster ikke", ok, true)
|
||||
if not ok then print(err) end
|
||||
|
||||
-- SVW7: udfoldet vare hvor to siloer bytter plads i mængde -> ingen genopbygning, rækkerne følger bygningen
|
||||
frame.currentTab = 7
|
||||
frame.expandedItems = nil
|
||||
frame:rebuild()
|
||||
frame:toggleItem(31)
|
||||
local subsBefore = frame.itemView.itemRows[1].subs
|
||||
ADSources.inventory[1].entries[1].level = 950 -- Farma #1 (300 -> 950) forbi Farma #2 (900)
|
||||
for _ = 1, 12 do frame:refreshLive() end
|
||||
check("SVW7 samme rækker (ingen genopbygning)", frame.itemView.itemRows[1].subs, subsBefore)
|
||||
check("SVW7 første underrække er stadig Farma #2", subsBefore[1].cells[1].text, "Farma #2")
|
||||
check("SVW7 anden underrække viser Farma #1's nye tal", subsBefore[2].cells[2].text:find("950", 1, true) ~= nil, true)
|
||||
|
||||
-- SVW6: alle brugte l10n-nøgler findes i begge sprogfiler
|
||||
for _, file in ipairs({"FS25_ADSmartPickup/l10n/l10n_da.xml", "FS25_ADSmartPickup/l10n/l10n_en.xml"}) do
|
||||
local content = io.open(file):read("*a")
|
||||
for key in pairs(i18nTexts) do
|
||||
if key:find("^spu_") and not content:find('name="' .. key .. '"', 1, true) then
|
||||
failures = failures + 1
|
||||
print("FAIL SVW6 mangler " .. key .. " i " .. file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||
os.exit(failures == 0 and 0 or 1)
|
||||
Reference in New Issue
Block a user