aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_formula.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_formula.py')
-rw-r--r--tests/test_formula.py96
1 files changed, 96 insertions, 0 deletions
diff --git a/tests/test_formula.py b/tests/test_formula.py
new file mode 100644
index 0000000..8074e5a
--- /dev/null
+++ b/tests/test_formula.py
@@ -0,0 +1,96 @@
+# generat3D - evaluate Daz DSON scenes and write .dbz files
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import unittest
+
+from generat3d import Error
+from generat3d.formula import Graph
+
+
+def push(key):
+ return {"op": "push", "key": key}
+
+
+def val(v):
+ return {"op": "push", "val": v}
+
+
+class GraphTest(unittest.TestCase):
+ def setUp(self):
+ self.g = Graph()
+ self.g.add_prop("a", 0.5)
+ self.g.add_prop("b")
+ self.g.add_prop("c", 0.0, 0.0, 1.0, clamped=True)
+
+ def test_base_values(self):
+ self.assertEqual(self.g.evaluate(), {"a": 0.5, "b": 0.0, "c": 0.0})
+
+ def test_sum_stage(self):
+ self.g.add_formula("b", [push("a"), val(2), {"op": "mult"}])
+ self.g.add_formula("b", [push("a"), val(1), {"op": "add"}])
+ self.assertEqual(self.g.evaluate()["b"], 1.0 + 1.5)
+
+ def test_mult_stage(self):
+ self.g.set_value("b", 3.0)
+ self.g.add_formula("b", [push("a")], stage="mult")
+ self.assertEqual(self.g.evaluate()["b"], 1.5)
+
+ def test_chain_and_clamp(self):
+ self.g.add_formula("b", [push("a"), val(4), {"op": "mult"}])
+ self.g.add_formula("c", [push("b")])
+ out = self.g.evaluate()
+ self.assertEqual(out["b"], 2.0)
+ self.assertEqual(out["c"], 1.0)
+
+ def test_sub_div_neg(self):
+ self.g.add_formula("b", [val(1), push("a"), {"op": "sub"}, val(4), {"op": "div"},
+ {"op": "neg"}])
+ self.assertEqual(self.g.evaluate()["b"], -0.125)
+
+ def test_unknown_input_drops_formula(self):
+ self.g.add_formula("b", [push("missing")])
+ self.g.add_formula("b", [val(7)])
+ self.assertEqual(self.g.evaluate()["b"], 7)
+
+ def test_output_without_prop_is_ignored(self):
+ self.g.add_formula("nowhere", [val(1)])
+ self.assertNotIn("nowhere", self.g.evaluate())
+
+ def test_cycle_is_fatal(self):
+ self.g.add_formula("a", [push("b")])
+ self.g.add_formula("b", [push("a")])
+ with self.assertRaisesRegex(Error, "formula cycle: a -> b -> a"):
+ self.g.evaluate()
+
+ def test_unknown_op_is_fatal(self):
+ self.g.add_formula("b", [val(1), {"op": "pow"}], source="/data/x.dsf#m")
+ with self.assertRaisesRegex(Error, r"/data/x.dsf#m: unknown formula operation 'pow'"):
+ self.g.evaluate()
+
+ def test_unknown_stage_is_fatal(self):
+ with self.assertRaisesRegex(Error, "unknown formula stage 'avg'"):
+ self.g.add_formula("b", [val(1)], stage="avg")
+
+ def test_stack_errors_are_fatal(self):
+ self.g.add_formula("b", [{"op": "add"}], source="f")
+ with self.assertRaisesRegex(Error, "f: malformed formula for b"):
+ self.g.evaluate()
+
+ def test_leftover_stack_is_fatal(self):
+ self.g.add_formula("b", [val(1), val(2)], source="f")
+ with self.assertRaisesRegex(Error, "f: malformed formula for b"):
+ self.g.evaluate()