worldgen

git clone https://git.godosa.eu/worldgen

master

raw ยท 6316 bytes

import shutil
import tempfile
import unittest
from pathlib import Path

from mapgen import config as C

from mapgen.testing import FIXTURE_TOML

TECT = """
[[plate]]
id = "a"
seed = [0.0, 0.0]
kind = "continental"
motion = [90.0, 3.0]
[[plate]]
id = "b"
seed = [0.0, 90.0]
kind = "oceanic"
motion = [270.0, 3.0]
"""


class ConfigTest(unittest.TestCase):
    def setUp(self):
        self.tmp = Path(tempfile.mkdtemp())
        (self.tmp / "config").mkdir()
        shutil.copy(FIXTURE_TOML, self.tmp / "config" / "world.toml")
        (self.tmp / "config" / "tectonics.toml").write_text(TECT)

    def tearDown(self):
        shutil.rmtree(self.tmp)

    def test_loads_repo_world(self):
        cfg, tect = C.load(self.tmp)
        self.assertEqual(cfg["planet"]["radius_km"], 12742.0)
        self.assertEqual(len(tect["plate"]), 2)

    def test_out_of_range_is_error(self):
        p = self.tmp / "config" / "world.toml"
        p.write_text(p.read_text().replace("tilt_deg = 20.0", "tilt_deg = 120.0"))
        with self.assertRaisesRegex(C.ConfigError, "tilt_deg"):
            C.load(self.tmp)

    def test_missing_key_is_error(self):
        p = self.tmp / "config" / "world.toml"
        p.write_text(p.read_text().replace("seed = 1296\n", ""))
        with self.assertRaisesRegex(C.ConfigError, "seed"):
            C.load(self.tmp)

    def test_params_merge_and_typo_guard(self):
        cfg = {"erosion": {"k": 0.5}}
        self.assertEqual(C.params(cfg, "erosion", {"k": 0.1, "m": 0.5}), {"k": 0.5, "m": 0.5})
        with self.assertRaisesRegex(C.ConfigError, "kk"):
            C.params({"erosion": {"kk": 1}}, "erosion", {"k": 0.1})

    def test_tectonics_validation(self):
        (self.tmp / "config" / "tectonics.toml").write_text(TECT.replace('kind = "oceanic"', 'kind = "lava"'))
        with self.assertRaisesRegex(C.ConfigError, "kind"):
            C.load(self.tmp)
        (self.tmp / "config" / "tectonics.toml").write_text(TECT.replace('id = "b"', 'id = "a"'))
        with self.assertRaisesRegex(C.ConfigError, "duplicate"):
            C.load(self.tmp)


REV = """
[[plateau]]
name = "p1"
center = [-40.0, -18.0]
area_km2 = 3.0e6
elongation = 1.9
azimuth_deg = 30.0
top_m = [1500.0, 3000.0]
[[plateau]]
name = "p2"
center = [0.0, 176.0]
area_km2 = 1.0e6
top_m = [1000.0, 1500.0]
islands = true
[[land_patch]]
name = "fill"
center = [-10.0, 102.0]
radius_km = 2850.0
edge_noise = 0.4
[[zone]]
name = "R1"
field = "o2"
center = [-5.6, -118.8]
radius_km = 3200.0
v = 1.0
"""
ERAS = """
[[event]]
name = "cut"
kind = "disintegrate"
center = [-10.0, 102.0]
radius_km = 3000.0
depth_m = 3000.0
[[event]]
name = "aura"
kind = "zone"
shape = "landmass"
seed = [-12.7, 121.5]
reach_km = [500.0, 1000.0]
edge_km = 5.0
fields = { gravity_g = 0.35, pressure_bar = 2.0, o2_fraction = 0.35, fire_reactivity = 0.5 }
[eras]
order = ["before", "after"]
default = "after"
[eras.before]
label = "Before"
events = []
[eras.after]
label = "After"
events = ["cut", "aura"]
"""


class RevisionConfigTest(unittest.TestCase):
    def setUp(self):
        self.tmp = Path(tempfile.mkdtemp())
        (self.tmp / "config").mkdir()
        shutil.copy(FIXTURE_TOML, self.tmp / "config" / "world.toml")
        self._set()

    def tearDown(self):
        shutil.rmtree(self.tmp)

    def _set(self, rev=REV, eras=ERAS):
        (self.tmp / "config" / "tectonics.toml").write_text(TECT + rev)
        (self.tmp / "config" / "eras.toml").write_text(eras)

    def test_valid_revision_config_loads(self):
        _, t = C.load(self.tmp)
        self.assertEqual([p["name"] for p in t["plateau"]], ["p1", "p2"])
        self.assertEqual([e["name"] for e in C.era_events(t, "after")], ["cut", "aura"])
        self.assertEqual(C.era_events(t, "before"), [])

    def test_era_events_are_cumulative(self):
        eras = ERAS.replace('order = ["before", "after"]', 'order = ["before", "mid", "after"]').replace(
            '[eras.after]\nlabel = "After"\nevents = ["cut", "aura"]',
            '[eras.mid]\nlabel = "Mid"\nevents = ["aura"]\n[eras.after]\nlabel = "After"\nevents = ["cut"]')
        self._set(REV, eras)
        _, t = C.load(self.tmp)
        self.assertEqual([e["name"] for e in C.era_events(t, "mid")], ["aura"])
        self.assertEqual([e["name"] for e in C.era_events(t, "after")], ["aura", "cut"])
        with self.assertRaises(C.ConfigError):
            C.era_events(t, "never")

    def test_errors_name_the_problem(self):
        dup = '[[plateau]]\nname = "p1"\ncenter = [60.0, 60.0]\narea_km2 = 1e6\ntop_m = [1000.0, 2000.0]\n'
        cases = [
            (REV.replace("top_m = [1500.0, 3000.0]", "top_m = [3000.0, 1500.0]"), ERAS, "top_m"),
            (REV.replace("center = [0.0, 176.0]", "center = [-41.0, -10.0]"), ERAS, "apart"),
            (REV + dup, ERAS, "duplicate"),
            (REV.replace('field = "o2"', 'field = "heat"'), ERAS, "field"),
            (REV, ERAS.replace("fire_reactivity = 0.5", "sparkle = 1.0"), "sparkle"),
            (REV, ERAS.replace('default = "after"', 'default = "later"'), "default"),
            (REV, ERAS.replace('events = ["cut", "aura"]', 'events = ["cut", "nope"]'), "nope"),
            (REV, ERAS.replace('kind = "disintegrate"', 'kind = "flood"'), "kind"),
            (REV, ERAS.replace("edge_km = 5.0\n", ""), "edge_km"),
        ]
        for rev, eras, needle in cases:
            with self.subTest(needle=needle):
                self._set(rev, eras)
                with self.assertRaises(C.ConfigError) as cm:
                    C.load(self.tmp)
                self.assertIn(needle, str(cm.exception))

    def test_events_belong_in_eras_toml(self):
        (self.tmp / "config" / "tectonics.toml").write_text(TECT + REV + ERAS)
        with self.assertRaises(C.ConfigError) as cm:
            C.load(self.tmp)
        self.assertIn("eras.toml", str(cm.exception))

    def test_eras_toml_is_not_part_of_the_base_inputs_key(self):
        from mapgen import pipeline as P
        k = P.inputs_key(self.tmp, 1)
        self._set(REV, ERAS.replace("fire_reactivity = 0.5", "fire_reactivity = 0.6"))
        self.assertEqual(P.inputs_key(self.tmp, 1), k, "tuning an event must not rebuild the base world")
        self._set(REV.replace("v = 1.0", "v = 0.9"), ERAS)
        self.assertNotEqual(P.inputs_key(self.tmp, 1), k)