# 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 contextlib import gzip import io import json import math import os import tempfile import unittest from pathlib import Path from generat3d import Error from generat3d.dbz import build, orientation_quat, write from generat3d.library import Library from generat3d.scene import load DATA = Path(__file__).parent / "data" class DbzTest(unittest.TestCase): def setUp(self): with contextlib.redirect_stderr(io.StringIO()): self.doc = build(load(DATA / "scene.duf", Library([DATA / "lib"])), [DATA / "lib"]) def test_header(self): self.assertEqual(self.doc["application"], "export_to_blender") self.assertEqual(self.doc["figures"][-1], {"name": "dummy", "num verts": 0}) def test_figure_entry(self): fig = self.doc["figures"][0] self.assertEqual((fig["name"], fig["id"], fig["label"], fig["class"]), ("Fig", "Fig", "Test Figure", "DzFigure")) self.assertEqual(fig["num verts"], 3) self.assertEqual(fig["vertices"][1], [1.5, 2.0, 0.0]) self.assertEqual(fig["material groups"], ["Skin"]) self.assertEqual(fig["orientation"], [0.0, 0.0, 0.0, 1.0]) self.assertNotIn("ws_transform", fig) def test_bones(self): hip = self.doc["figures"][0]["bones"][0] self.assertEqual(hip["name"], "hip") self.assertEqual(hip["center_point"], [0, 7, 0]) self.assertEqual(hip["origin"], [0, 7, 0]) self.assertEqual(hip["rotation_order"], "YZX") def test_orientation_quat(self): x, y, z, w = orientation_quat([90, 0, 0]) self.assertAlmostEqual(x, math.sqrt(0.5)) self.assertAlmostEqual(w, math.sqrt(0.5)) self.assertAlmostEqual(y, 0) self.assertAlmostEqual(z, 0) def test_write_is_gzip_json_and_atomic(self): with tempfile.TemporaryDirectory() as d: out = Path(d) / "s.dbz" write(self.doc, out) self.assertEqual(json.loads(gzip.decompress(out.read_bytes())), self.doc) self.assertEqual([p.name for p in Path(d).iterdir()], ["s.dbz"]) def test_failed_write_leaves_nothing(self): with tempfile.TemporaryDirectory() as d: out = Path(d) / "s.dbz" with self.assertRaises(TypeError): write({"bad": object()}, out) self.assertEqual(list(Path(d).iterdir()), []) def test_os_error_becomes_error(self): with tempfile.TemporaryDirectory() as d: with self.assertRaisesRegex(Error, "cannot write"): write(self.doc, Path(d) / "missing" / "s.dbz") def test_instance_name_and_label_on_entry(self): with tempfile.TemporaryDirectory() as d: duf = Path(d) / "s.duf" duf.write_text(json.dumps({"scene": {"nodes": [ {"id": "Fig", "url": "/data/Test/Fig/Fig.dsf#Fig", "name": "Alice", "label": "Alice Label"}]}})) with contextlib.redirect_stderr(io.StringIO()): doc = build(load(duf, Library([DATA / "lib"])), [DATA / "lib"]) fig = doc["figures"][0] self.assertEqual((fig["name"], fig["label"], fig["id"]), ("Alice", "Alice Label", "Fig")) def test_write_mode_follows_umask(self): with tempfile.TemporaryDirectory() as d: out = Path(d) / "s.dbz" write(self.doc, out) m = os.umask(0) os.umask(m) self.assertEqual(out.stat().st_mode & 0o777, 0o666 & ~m)