Files
Lena feb4500c6c small update
boss prototype, auto planter, visual reworks, bug fixes
2026-08-29 10:45:34 +02:00

409 lines
17 KiB
Python

import atexit
import os
import pathlib
import tempfile
import time
import unittest
# Point the engine at a fresh empty save to keep these tests hermetic.
import engine.collection as collection
_fd, _tmp_path = tempfile.mkstemp(suffix=".json")
os.close(_fd)
_TMP = pathlib.Path(_tmp_path)
_ORIG_DATA_FILE = collection._DATA_FILE
collection._DATA_FILE = _TMP
@atexit.register
def _restore_collection():
collection._DATA_FILE = _ORIG_DATA_FILE
if _TMP.exists():
_TMP.unlink()
class SoilTest(unittest.TestCase):
def setUp(self):
data = collection.load()
data["owned"] = ["Rose", "Lavender", "Zinnia"]
data["soil"] = {"seed_cd_until": 0.0, "seeds": [], "plots": []}
data["water"] = 10
data["fertilizer"] = 10
collection.save(data)
def test_retrieve_seed(self):
self.assertEqual(collection.retrieve_seed("Rose"), "")
state = collection.soil_state()
self.assertEqual(len(state["seeds"]), 1)
self.assertEqual(state["seeds"][0]["plant"], "Rose")
self.assertGreater(state["cd_left"], 0) # cooldown set
def test_retrieve_respects_cooldown(self):
collection.retrieve_seed("Rose")
self.assertNotEqual(collection.retrieve_seed("Lavender"), "")
state = collection.soil_state()
self.assertEqual(len(state["seeds"]), 1)
def test_retrieve_requires_owned(self):
self.assertNotEqual(collection.retrieve_seed("NotAPlant"), "")
data = collection.load()
data["discovered"] = ["Marigold"]
collection.save(data)
self.assertNotEqual(collection.retrieve_seed("Marigold"), "")
def test_plant_creates_stage_zero_plot(self):
collection.retrieve_seed("Rose")
self.assertEqual(collection.plant_seed(0), "")
state = collection.soil_state()
self.assertEqual(len(state["plots"]), 1)
self.assertEqual(state["seeds"], [])
plot = state["plots"][0]
self.assertEqual(plot["stage"], 0)
self.assertFalse(plot["grown"])
self.assertFalse(plot["stalled"]) # stage not yet full
def test_stage_fills_over_time(self):
data = collection.load()
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
state = collection.soil_state()
plot = state["plots"][0]
self.assertEqual(plot["stage_progress"], 1.0)
self.assertTrue(plot["stalled"]) # stage full -> needs water
self.assertFalse(plot["grown"]) # but not the last stage
def test_water_requires_stalled(self):
collection.retrieve_seed("Rose")
collection.plant_seed(0)
self.assertNotEqual(collection.water_plot(0), "") # not stalled yet
def test_water_advances_stage(self):
data = collection.load()
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
self.assertEqual(collection.water_plot(0), "")
self.assertEqual(collection.available_fertilizer(), 9)
state = collection.soil_state()
self.assertEqual(state["plots"][0]["stage"], 1)
self.assertEqual(state["plots"][0]["water"], 1)
self.assertFalse(state["plots"][0]["stalled"]) # new stage just started
def test_water_requires_fertilizer_pool(self):
data = collection.load()
data["fertilizer"] = 0
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
self.assertNotEqual(collection.water_plot(0), "")
def test_harvest_requires_grown(self):
collection.retrieve_seed("Rose")
collection.plant_seed(0)
self.assertNotEqual(collection.harvest_plot(0), "") # not grown
def test_grow_all_stages_then_harvest(self):
# Rose is super-rare -> 4 stages. Plant with an old stage_started_at so each
# stage is instantly full, then water through stages 0..2.
data = collection.load()
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
total = collection.GROWTH_STAGES["super_rare"]
for expected in range(total - 1):
self.assertNotEqual(collection.harvest_plot(0), "") # not grown mid-way
self.assertEqual(collection.water_plot(0), "", f"water to advance to {expected + 1}")
# each watered stage is instantly full again (stage_started_at=now)
data = collection.load()
data["soil"]["plots"][0]["stage_started_at"] = 0
collection.save(data)
state = collection.soil_state()
self.assertTrue(state["plots"][0]["grown"])
dup_before = collection.get_progress("Rose")["duplicates"]
self.assertEqual(collection.harvest_plot(0), "")
state = collection.soil_state()
self.assertEqual(state["plots"], [])
self.assertEqual(collection.get_progress("Rose")["duplicates"], dup_before + 1)
def test_higher_rarity_more_stages(self):
self.assertGreater(collection.GROWTH_STAGES["super_super_rare"],
collection.GROWTH_STAGES["super_rare"])
self.assertGreater(collection.GROWTH_STAGES["super_rare"],
collection.GROWTH_STAGES["rare"])
self.assertGreater(collection.GROWTH_STAGES["rare"],
collection.GROWTH_STAGES["common"])
def test_default_one_plot_unlocked(self):
state = collection.soil_state()
self.assertEqual(state["plots_unlocked"], 1)
self.assertEqual(state["plots_max"], collection.SOIL_PLOTS_MAX)
def test_soil_gated_by_owned_plants(self):
data = collection.load()
data["owned"] = ["Rose", "Lavender"]
collection.save(data)
self.assertFalse(collection.soil_unlocked())
data = collection.load()
data["owned"] = [f"Plant{i}" for i in range(collection.SOIL_GATE_PLANTS)]
for n in data["owned"]:
data["progress"].setdefault(n, {"level": 1, "stage": 1, "xp": 0,
"duplicates": 0, "watered_at": 0})
collection.save(data)
self.assertTrue(collection.soil_unlocked())
def test_plot_unlock_costs_escalate(self):
self.assertEqual(collection.plot_unlock_cost(2), (10, 5))
self.assertEqual(collection.plot_unlock_cost(3), (50, 20))
self.assertEqual(collection.plot_unlock_cost(4), (100, 50))
self.assertEqual(collection.plot_unlock_cost(5), (0, 0))
def test_plant_limited_to_unlocked_plots(self):
data = collection.load()
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
# 1 unlocked, 1 planted -> planting another seed fails
collection.retrieve_seed("Lavender")
self.assertEqual(len(collection.soil_state()["seeds"]), 1)
self.assertNotEqual(collection.plant_seed(0), "")
def test_unlock_plot_spends_coins_and_fertilizer(self):
data = collection.load()
data["coins"] = 10
data["fertilizer"] = 5
collection.save(data)
self.assertEqual(collection.unlock_plot(), "")
self.assertEqual(collection.soil_state()["plots_unlocked"], 2)
self.assertEqual(collection.available_coins(), 0)
self.assertEqual(collection.available_fertilizer(), 0)
# too little fertilizer now
self.assertNotEqual(collection.unlock_plot(), "")
def test_unlock_plot_requires_coins(self):
data = collection.load()
data["coins"] = 0
data["fertilizer"] = 50
collection.save(data)
self.assertNotEqual(collection.unlock_plot(), "")
def test_unlock_plot_requires_fertilizer(self):
data = collection.load()
data["coins"] = 50
data["fertilizer"] = 4
collection.save(data)
self.assertNotEqual(collection.unlock_plot(), "")
def test_unlock_plot_max_four(self):
data = collection.load()
data["coins"] = 500
data["fertilizer"] = 500
collection.save(data)
for _ in range(3):
self.assertEqual(collection.unlock_plot(), "")
self.assertEqual(collection.soil_state()["plots_unlocked"], collection.SOIL_PLOTS_MAX)
self.assertNotEqual(collection.unlock_plot(), "") # capped
def test_after_unlock_extra_plot_usable(self):
data = collection.load()
data["coins"] = 10
data["fertilizer"] = 5
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
collection.unlock_plot() # now 2 unlocked, 1 used
collection.retrieve_seed("Lavender")
self.assertEqual(collection.plant_seed(0), "")
self.assertEqual(len(collection.soil_state()["plots"]), 2)
class AutoFarmerTest(unittest.TestCase):
def setUp(self):
data = collection.load()
data["owned"] = ["Rose", "Lavender", "Zinnia"]
data["soil"] = {"seed_cd_until": 0.0, "seeds": [], "plots": []}
data["fertilizer"] = 10
data["coins"] = 100
data["auto_farmer"] = False
data["auto_planter"] = False
data["auto_planter_active"] = False
collection.save(data)
def _stalled_plot(self):
data = collection.load()
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
def test_accrue_fertilizer_grants_one_per_hour(self):
data = collection.load()
data["fertilizer_accrued_at"] = time.time() - 7200 # 2 hours ago
data["fertilizer"] = 0
collection.save(data)
self.assertEqual(collection.accrue_fertilizer(), 2)
self.assertEqual(collection.available_fertilizer(), 2)
def test_accrue_fertilizer_caps(self):
data = collection.load()
data["fertilizer_accrued_at"] = time.time() - 7200
data["fertilizer"] = 99
collection.save(data)
self.assertEqual(collection.accrue_fertilizer(), 1)
self.assertEqual(collection.available_fertilizer(), 100)
def test_add_fertilizer_caps(self):
data = collection.load()
data["fertilizer"] = 99
collection.save(data)
collection.add_fertilizer(10)
self.assertEqual(collection.available_fertilizer(), 100)
def test_buy_auto_farmer(self):
self.assertEqual(collection.buy_auto_farmer(), "")
self.assertTrue(collection.auto_farmer_owned())
self.assertEqual(collection.available_coins(), 0)
self.assertNotEqual(collection.buy_auto_farmer(), "") # already owned
def test_buy_auto_farmer_requires_coins(self):
data = collection.load()
data["coins"] = 50
collection.save(data)
self.assertNotEqual(collection.buy_auto_farmer(), "")
self.assertFalse(collection.auto_farmer_owned())
def test_buy_activates_farmer(self):
self.assertEqual(collection.buy_auto_farmer(), "")
self.assertTrue(collection.auto_farmer_active())
def test_toggle_auto_farmer(self):
collection.buy_auto_farmer()
self.assertTrue(collection.auto_farmer_active())
self.assertFalse(collection.toggle_auto_farmer()) # turned off
self.assertFalse(collection.auto_farmer_active())
self.assertTrue(collection.toggle_auto_farmer()) # back on
self.assertTrue(collection.auto_farmer_owned()) # still owns it
def test_auto_farm_respects_toggle(self):
self._stalled_plot()
collection.buy_auto_farmer()
collection.toggle_auto_farmer() # turn it off
self.assertEqual(collection.auto_farm(), "Auto farmer off")
self.assertEqual(collection.available_fertilizer(), 10) # nothing spent
self.assertEqual(collection.soil_state()["plots"][0]["stage"], 0)
def test_auto_farm_fertilizes_stalled_plot(self):
self._stalled_plot()
self.assertEqual(collection.buy_auto_farmer(), "")
self.assertEqual(collection.auto_farm(), "")
self.assertEqual(collection.available_fertilizer(), 9)
self.assertEqual(collection.soil_state()["plots"][0]["stage"], 1)
def test_auto_farm_requires_farmer(self):
self._stalled_plot()
self.assertEqual(collection.auto_farm(), "No auto farmer")
def test_auto_farm_does_not_plant_or_harvest(self):
data = collection.load()
data["soil"]["plots"] = [
{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}, # stalled
{"plant": "Rose", "stage_started_at": 0, "stage": 3, "water": 0}, # grown (epic)
]
data["soil"]["seeds"] = [{"plant": "Lavender"}]
collection.save(data)
self.assertEqual(collection.buy_auto_farmer(), "")
self.assertEqual(collection.auto_farm(), "")
state = collection.soil_state()
self.assertEqual(len(state["plots"]), 2) # grown plot not harvested
self.assertEqual(len(state["seeds"]), 1) # seed not planted
self.assertEqual(state["plots"][0]["stage"], 1) # stalled plot advanced
def test_buy_auto_planter(self):
data = collection.load()
data["coins"] = 300
collection.save(data)
self.assertEqual(collection.buy_auto_planter(), "")
self.assertTrue(collection.auto_planter_owned())
self.assertTrue(collection.auto_planter_active())
self.assertEqual(collection.available_coins(), 100)
self.assertNotEqual(collection.buy_auto_planter(), "") # already owned
def test_buy_auto_planter_requires_coins(self):
data = collection.load()
data["coins"] = 50
collection.save(data)
self.assertNotEqual(collection.buy_auto_planter(), "")
self.assertFalse(collection.auto_planter_owned())
def test_toggle_auto_planter(self):
data = collection.load()
data["coins"] = 300
collection.save(data)
collection.buy_auto_planter()
self.assertFalse(collection.toggle_auto_planter()) # turned off
self.assertFalse(collection.auto_planter_active())
self.assertTrue(collection.toggle_auto_planter()) # back on
def test_auto_plant_plants_seed_into_empty_plot(self):
data = collection.load()
data["coins"] = 300
data["soil"]["seeds"] = [{"plant": "Rose"}, {"plant": "Lavender"}]
collection.save(data)
self.assertEqual(collection.buy_auto_planter(), "")
self.assertEqual(collection.auto_plant(), "")
state = collection.soil_state()
self.assertEqual(len(state["plots"]), 1)
self.assertEqual(state["plots"][0]["plant"], "Rose") # first seed planted
self.assertEqual(len(state["seeds"]), 1)
def test_auto_plant_respects_toggle_and_requirements(self):
data = collection.load()
data["coins"] = 300
data["soil"]["seeds"] = [{"plant": "Rose"}]
collection.save(data)
self.assertEqual(collection.auto_plant(), "No auto planter") # not owned
collection.buy_auto_planter()
collection.toggle_auto_planter() # turn off
self.assertEqual(collection.auto_plant(), "Auto planter off")
collection.toggle_auto_planter()
data = collection.load()
data["soil"]["seeds"] = []
collection.save(data)
self.assertEqual(collection.auto_plant(), "") # nothing to plant -> idle
self.assertEqual(len(collection.soil_state()["plots"]), 0)
def test_auto_plant_wont_overfill_plots(self):
data = collection.load()
data["coins"] = 300
data["soil"]["seeds"] = [{"plant": "Rose"}, {"plant": "Lavender"}]
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
collection.save(data)
self.assertEqual(collection.buy_auto_planter(), "")
self.assertEqual(collection.auto_plant(), "") # no free plot -> idle
self.assertEqual(len(collection.soil_state()["plots"]), 1)
class PackShopTest(unittest.TestCase):
def setUp(self):
data = collection.load()
data["coins"] = 500
data["packs"] = {t: 0 for t in collection.PACK_TYPES}
collection.save(data)
def test_buy_lesser_and_general(self):
self.assertEqual(collection.buy_pack("lesser"), "")
self.assertEqual(collection.buy_pack("general"), "")
self.assertEqual(collection.buy_pack("garden"), "")
self.assertEqual(collection.available_coins(), 500 - 50 - 100 - 100)
packs = collection.pack_counts()
self.assertEqual(packs["lesser"], 1)
self.assertEqual(packs["general"], 1)
self.assertEqual(packs["garden"], 1)
def test_tower_pack_cannot_be_bought(self):
self.assertEqual(collection.buy_pack("tower"), "That pack can't be bought")
self.assertEqual(collection.pack_counts().get("tower", 0), 0)
self.assertEqual(collection.available_coins(), 500)
def test_buy_pack_requires_coins(self):
data = collection.load()
data["coins"] = 30
collection.save(data)
self.assertEqual(collection.buy_pack("lesser"), "Not enough coins")
self.assertEqual(collection.pack_counts().get("lesser", 0), 0)
if __name__ == "__main__":
unittest.main()