# SPDX-FileCopyrightText: 2020-2023 Blender Authors # # SPDX-License-Identifier: GPL-2.0-or-later import unittest import bpy import pathlib import sys from rna_prop_ui import rna_idprop_quote_path """ blender -b --factory-startup --python tests/python/bl_animation_drivers.py -- --testdir /path/to/tests/data/animation """ class AbstractEmptyDriverTest: def setUp(self): super().setUp() bpy.ops.wm.read_homefile(use_factory_startup=True) self.obj = bpy.data.objects['Cube'] def assertPropValue(self, prop_name, value): self.assertEqual(self.obj[prop_name], value) def _make_context_driver(obj, prop_name, ctx_type, ctx_path, index=None, fallback=None, force_python=False): obj[prop_name] = 0 fcu = obj.driver_add(rna_idprop_quote_path(prop_name), -1) drv = fcu.driver if force_python: # Expression that requires full python interpreter drv.type = 'SCRIPTED' drv.expression = '[var][0]' else: drv.type = 'SUM' var = drv.variables.new() var.name = "var" var.type = 'CONTEXT_PROP' tgt = var.targets[0] tgt.context_property = ctx_type tgt.data_path = rna_idprop_quote_path(ctx_path) + (f"[{index}]" if index is not None else "") if fallback is not None: tgt.use_fallback_value = True tgt.fallback_value = fallback return fcu def _is_fallback_used(fcu): return fcu.driver.variables[0].targets[0].is_fallback_used class ContextSceneDriverTest(AbstractEmptyDriverTest, unittest.TestCase): """ Ensure keying things by name or with a keying set adds the right keys. """ def setUp(self): super().setUp() bpy.context.scene["test_property"] = 123 def test_context_valid(self): fcu = _make_context_driver( self.obj, 'test_valid', 'ACTIVE_SCENE', 'test_property') bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertPropValue('test_valid', 123) def test_context_invalid(self): fcu = _make_context_driver( self.obj, 'test_invalid', 'ACTIVE_SCENE', 'test_property_bad') bpy.context.view_layer.update() self.assertFalse(fcu.driver.is_valid) def test_context_fallback(self): fcu = _make_context_driver( self.obj, 'test_fallback', 'ACTIVE_SCENE', 'test_property_bad', fallback=321) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertTrue(_is_fallback_used(fcu)) self.assertPropValue('test_fallback', 321) def test_context_fallback_valid(self): fcu = _make_context_driver( self.obj, 'test_fallback_valid', 'ACTIVE_SCENE', 'test_property', fallback=321) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertFalse(_is_fallback_used(fcu)) self.assertPropValue('test_fallback_valid', 123) def test_context_fallback_python(self): fcu = _make_context_driver( self.obj, 'test_fallback_py', 'ACTIVE_SCENE', 'test_property_bad', fallback=321, force_python=True) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertTrue(_is_fallback_used(fcu)) self.assertPropValue('test_fallback_py', 321) class ContextSceneArrayDriverTest(AbstractEmptyDriverTest, unittest.TestCase): """ Ensure keying things by name or with a keying set adds the right keys. """ def setUp(self): super().setUp() bpy.context.scene["test_property"] = [123, 456] def test_context_valid(self): fcu = _make_context_driver( self.obj, 'test_valid', 'ACTIVE_SCENE', 'test_property', index=0) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertPropValue('test_valid', 123) def test_context_invalid(self): fcu = _make_context_driver( self.obj, 'test_invalid', 'ACTIVE_SCENE', 'test_property', index=2) bpy.context.view_layer.update() self.assertFalse(fcu.driver.is_valid) def test_context_fallback(self): fcu = _make_context_driver( self.obj, 'test_fallback', 'ACTIVE_SCENE', 'test_property', index=2, fallback=321) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertTrue(_is_fallback_used(fcu)) self.assertPropValue('test_fallback', 321) def test_context_fallback_valid(self): fcu = _make_context_driver( self.obj, 'test_fallback_valid', 'ACTIVE_SCENE', 'test_property', index=0, fallback=321) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertFalse(_is_fallback_used(fcu)) self.assertPropValue('test_fallback_valid', 123) def test_context_fallback_python(self): fcu = _make_context_driver( self.obj, 'test_fallback_py', 'ACTIVE_SCENE', 'test_property', index=2, fallback=321, force_python=True) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertTrue(_is_fallback_used(fcu)) self.assertPropValue('test_fallback_py', 321) def _select_view_layer(index): bpy.context.window.view_layer = bpy.context.scene.view_layers[index] class ContextViewLayerDriverTest(AbstractEmptyDriverTest, unittest.TestCase): """ Ensure keying things by name or with a keying set adds the right keys. """ def setUp(self): super().setUp() bpy.ops.scene.view_layer_add(type='COPY') scene = bpy.context.scene scene.view_layers[0]['test_property'] = 123 scene.view_layers[1]['test_property'] = 456 _select_view_layer(0) def test_context_valid(self): fcu = _make_context_driver( self.obj, 'test_valid', 'ACTIVE_VIEW_LAYER', 'test_property') _select_view_layer(0) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertPropValue('test_valid', 123) _select_view_layer(1) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertPropValue('test_valid', 456) def test_context_fallback(self): del bpy.context.scene.view_layers[1]['test_property'] fcu = _make_context_driver( self.obj, 'test_fallback', 'ACTIVE_VIEW_LAYER', 'test_property', fallback=321) _select_view_layer(0) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertFalse(_is_fallback_used(fcu)) self.assertPropValue('test_fallback', 123) _select_view_layer(1) bpy.context.view_layer.update() self.assertTrue(fcu.driver.is_valid) self.assertTrue(_is_fallback_used(fcu)) self.assertPropValue('test_fallback', 321) def main(): global args import argparse if '--' in sys.argv: argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] else: argv = sys.argv parser = argparse.ArgumentParser() parser.add_argument('--testdir', required=True, type=pathlib.Path) args, remaining = parser.parse_known_args(argv) unittest.main(argv=remaining) if __name__ == "__main__": main()