# generat3D - evaluate Daz DSON scenes and write .dbz files # Copyright (C) 2026 Danilo M. # # 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()