409 lines
17 KiB
Python
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() |