1# Copyright (c) 2008-19, Enthought, Inc. 2# All rights reserved. 3# 4# This software is provided without warranty under the terms of the BSD 5# license included in LICENSE.txt and may be redistributed only 6# under the conditions described in the aforementioned license. The license 7# is also available online at http://www.enthought.com/licenses/BSD.txt 8# 9# Thanks for using Enthought open source! 10# 11# Author: Corran Webster 12# Date: August 12, 2019 13 14import unittest 15 16from traits.api import Any, Bool, Event, Float, HasTraits, Int, List, Undefined 17from traits.trait_base import xgetattr 18 19from traitsui.context_value import ContextValue, CVFloat, CVInt 20from traitsui.editor import Editor 21from traitsui.editor_factory import EditorFactory 22from traitsui.handler import default_handler 23from traitsui.ui import UI 24from traitsui.tests._tools import ( 25 BaseTestMixin, GuiTestAssistant, no_gui_test_assistant 26) 27 28 29class FakeControl(HasTraits): 30 """ A pure Traits object that fakes being a control. 31 32 It can aither hold a value (mimicking a field), or have an event 33 which is listened to (mimicking a button or similar control). 34 """ 35 36 #: The value stored in the control. 37 control_value = Any() 38 39 #: An event which also can be fired. 40 control_event = Event() 41 42 43class StubEditorFactory(EditorFactory): 44 """ A minimal editor factory 45 46 This simply holds state that may or may not be copied to the 47 editor. No attempt is made to handle custom/readonly/etc. 48 variation. 49 """ 50 51 #: Whether or not the traits are events. 52 is_event = Bool() 53 54 #: Whether or not the traits are events. 55 auxiliary_value = Any(sync_value=True) 56 57 #: Whether or not the traits are events. 58 auxiliary_cv_int = CVInt 59 60 #: Whether or not the traits are events. 61 auxiliary_cv_float = CVFloat 62 63 def _auxiliary_cv_int_default(self): 64 return 0 65 66 def _auxiliary_cv_float_default(self): 67 return 0.0 68 69 70class StubEditor(Editor): 71 """ A minimal editor implementaton for a StubEditorFactory. 72 73 The editor creates a FakeControl instance as its control object 74 and keeps values synchronized either to `control_value` or 75 `control_event` (if `is_event` is True). 76 """ 77 78 #: Whether or not the traits are events. 79 is_event = Bool() 80 81 #: An auxiliary value we want to synchronize. 82 auxiliary_value = Any() 83 84 #: An auxiliary list we want to synchronize. 85 auxiliary_list = List() 86 87 #: An auxiliary event we want to synchronize. 88 auxiliary_event = Event() 89 90 #: An auxiliary int we want to synchronize with a context value. 91 auxiliary_cv_int = Int(sync_value="from") 92 93 #: An auxiliary float we want to synchronize with a context value. 94 auxiliary_cv_float = Float() 95 96 def init(self, parent): 97 self.control = FakeControl() 98 self.is_event = self.factory.is_event 99 if self.is_event: 100 self.control.on_trait_change(self.update_object, "control_event") 101 else: 102 self.control.on_trait_change(self.update_object, "control_value") 103 104 def dispose(self): 105 if self.is_event: 106 self.control.on_trait_change( 107 self.update_object, "control_event", remove=True 108 ) 109 else: 110 self.control.on_trait_change( 111 self.update_object, "control_value", remove=True 112 ) 113 super(StubEditor, self).dispose() 114 115 def update_editor(self): 116 if self.is_event: 117 self.control.control_event = True 118 else: 119 self.control.control_value = self.value 120 121 def update_object(self, new): 122 if self.control is not None: 123 if not self._no_update: 124 self._no_update = True 125 try: 126 self.value = new 127 finally: 128 self._no_update = False 129 130 def set_focus(self, parent): 131 pass 132 133 134class UserObject(HasTraits): 135 """ A simple HasTraits class with a variety of state. """ 136 137 #: The value being edited. 138 user_value = Any("test") 139 140 #: An auxiliary user value 141 user_auxiliary = Any(10) 142 143 #: An list user value 144 user_list = List(["one", "two", "three"]) 145 146 #: An event user value 147 user_event = Event() 148 149 #: A state that is to be synchronized with the editor. 150 invalid_state = Bool() 151 152 153def create_editor( 154 context=None, 155 object_name="object", 156 name="user_value", 157 factory=None, 158 is_event=False, 159): 160 if context is None: 161 user_object = UserObject() 162 context = {"object": user_object} 163 elif "." in object_name: 164 context_name, xname = object_name.split(".", 1) 165 context_object = context[context_name] 166 user_object = xgetattr(context_object, xname) 167 else: 168 user_object = context[object_name] 169 ui = UI(context=context, handler=default_handler()) 170 171 if factory is None: 172 factory = StubEditorFactory() 173 factory.is_event = is_event 174 175 editor = StubEditor( 176 parent=None, 177 ui=ui, 178 object_name=object_name, 179 name=name, 180 factory=factory, 181 object=user_object, 182 ) 183 return editor 184 185 186@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") 187class TestEditor(BaseTestMixin, GuiTestAssistant, unittest.TestCase): 188 189 def setUp(self): 190 BaseTestMixin.setUp(self) 191 GuiTestAssistant.setUp(self) 192 193 def tearDown(self): 194 GuiTestAssistant.tearDown(self) 195 BaseTestMixin.tearDown(self) 196 197 def change_user_value(self, editor, object, name, value): 198 if editor.is_event: 199 control_name = "control_event" 200 else: 201 control_name = "control_value" 202 203 # test the value in the control changes 204 with self.assertTraitChanges(editor.control, control_name, count=1): 205 self.gui.set_trait_later(object, name, value) 206 self.event_loop_helper.event_loop_with_timeout(repeat=6) 207 208 def change_control_value(self, editor, object, name, value): 209 if editor.is_event: 210 control_name = "control_event" 211 else: 212 control_name = "control_value" 213 214 # test the value in the user object changes 215 with self.assertTraitChanges(object, name, count=1): 216 self.gui.set_trait_later(editor.control, control_name, value) 217 self.event_loop_helper.event_loop_with_timeout(repeat=6) 218 219 def test_lifecycle(self): 220 editor = create_editor() 221 222 self.assertEqual(editor.old_value, "test") 223 self.assertEqual(editor.name, "user_value") 224 self.assertEqual(editor.extended_name, "user_value") 225 self.assertEqual(editor.value, "test") 226 self.assertEqual(editor.str_value, "test") 227 self.assertIs(editor.value_trait, editor.object.trait("user_value")) 228 self.assertIs(editor.context_object, editor.ui.context["object"]) 229 230 editor.prepare(None) 231 232 # preparation creates the control and sets the control value 233 self.assertEqual(editor.value, "test") 234 self.assertEqual(editor.control.control_value, "test") 235 236 with self.assertTraitChanges(editor.ui, "modified", count=1): 237 self.change_user_value( 238 editor, editor.object, "user_value", "new test" 239 ) 240 241 self.assertEqual(editor.value, "new test") 242 self.assertEqual(editor.control.control_value, "new test") 243 self.assertTrue(editor.ui.modified) 244 245 self.change_control_value( 246 editor, editor.object, "user_value", "even newer test" 247 ) 248 249 self.assertEqual(editor.value, "even newer test") 250 self.assertEqual(editor.object.user_value, "even newer test") 251 252 editor.dispose() 253 254 self.assertIsNone(editor.object) 255 self.assertIsNone(editor.factory) 256 self.assertIsNone(editor.control) 257 258 def test_context_object(self): 259 user_object = UserObject(user_value="other_test") 260 context = {"object": UserObject(), "other_object": user_object} 261 editor = create_editor( 262 context=context, object_name="other_object" 263 ) 264 265 self.assertEqual(editor.old_value, "other_test") 266 self.assertEqual(editor.name, "user_value") 267 self.assertEqual(editor.extended_name, "user_value") 268 self.assertIs(editor.context_object, editor.ui.context["other_object"]) 269 270 editor.prepare(None) 271 272 # preparation creates the control and sets the control value 273 self.assertEqual(editor.value, "other_test") 274 self.assertEqual(editor.control.control_value, "other_test") 275 276 self.change_user_value(editor, user_object, "user_value", "new test") 277 278 self.assertEqual(editor.value, "new test") 279 self.assertEqual(editor.control.control_value, "new test") 280 281 self.change_control_value( 282 editor, user_object, "user_value", "even newer test" 283 ) 284 285 self.assertEqual(editor.value, "even newer test") 286 self.assertEqual(editor.object.user_value, "even newer test") 287 288 editor.dispose() 289 290 def test_event_trait(self): 291 editor = create_editor(name="user_event", is_event=True) 292 user_object = editor.object 293 294 self.assertEqual(editor.name, "user_event") 295 self.assertEqual(editor.extended_name, "user_event") 296 self.assertIs(editor.context_object, editor.ui.context["object"]) 297 298 editor.prepare(None) 299 300 # preparation creates the control and sets the control value 301 self.assertIs(editor.value, Undefined) 302 303 self.change_user_value(editor, user_object, "user_event", True) 304 self.change_control_value(editor, user_object, "user_event", True) 305 306 editor.dispose() 307 308 def test_chained_object(self): 309 context = { 310 "object": UserObject( 311 user_auxiliary=UserObject(user_value="other_test") 312 ) 313 } 314 user_object = context["object"].user_auxiliary 315 editor = create_editor( 316 context=context, object_name="object.user_auxiliary" 317 ) 318 319 self.assertEqual(editor.old_value, "other_test") 320 self.assertEqual(editor.name, "user_value") 321 self.assertEqual(editor.extended_name, "user_auxiliary.user_value") 322 self.assertIs(editor.context_object, editor.ui.context["object"]) 323 324 editor.prepare(None) 325 326 # preparation creates the control and sets the control value 327 self.assertEqual(editor.value, "other_test") 328 self.assertEqual(editor.control.control_value, "other_test") 329 330 self.change_user_value(editor, user_object, "user_value", "new test") 331 332 self.assertEqual(editor.value, "new test") 333 self.assertEqual(editor.control.control_value, "new test") 334 335 self.change_control_value( 336 editor, user_object, "user_value", "even newer test" 337 ) 338 339 self.assertEqual(editor.value, "even newer test") 340 self.assertEqual(editor.object.user_value, "even newer test") 341 342 # test changing the chained object 343 new_user_object = UserObject(user_value="new object") 344 with self.assertTraitChanges(editor, "object", count=1): 345 self.change_user_value( 346 editor, context["object"], "user_auxiliary", new_user_object 347 ) 348 349 self.assertEqual(editor.value, "new object") 350 self.assertIs(editor.object, new_user_object) 351 self.assertEqual(editor.object.user_value, "new object") 352 353 editor.dispose() 354 355 def test_factory_sync_simple(self): 356 factory = StubEditorFactory(auxiliary_value="test") 357 editor = create_editor(factory=factory) 358 editor.prepare(None) 359 360 # preparation copies the auxiliary value from the factory 361 self.assertIs(editor.auxiliary_value, "test") 362 363 editor.dispose() 364 365 def test_factory_sync_cv_simple(self): 366 factory = StubEditorFactory() 367 editor = create_editor(factory=factory) 368 editor.prepare(None) 369 370 # preparation copies the auxiliary CV int value from the factory 371 self.assertIs(editor.auxiliary_cv_int, 0) 372 373 editor.dispose() 374 375 def test_parse_extended_name(self): 376 context = { 377 "object": UserObject( 378 user_auxiliary=UserObject(user_value="other_test") 379 ), 380 "other_object": UserObject(user_value="another_test"), 381 } 382 editor = create_editor(context=context) 383 editor.prepare(None) 384 385 # test simple name 386 object, name, getter = editor.parse_extended_name("user_value") 387 value = getter() 388 389 self.assertIs(object, context["object"]) 390 self.assertEqual(name, "user_value") 391 self.assertEqual(value, "test") 392 393 # test different context object name 394 object, name, getter = editor.parse_extended_name( 395 "other_object.user_value" 396 ) 397 value = getter() 398 399 self.assertIs(object, context["other_object"]) 400 self.assertEqual(name, "user_value") 401 self.assertEqual(value, "another_test") 402 403 # test chained name 404 object, name, getter = editor.parse_extended_name( 405 "object.user_auxiliary.user_value" 406 ) 407 value = getter() 408 409 self.assertIs(object, context["object"]) 410 self.assertEqual(name, "user_auxiliary.user_value") 411 self.assertEqual(value, "other_test") 412 413 editor.dispose() 414 415 # Test synchronizing built-in trait values between factory 416 # and editor. 417 418 def test_factory_sync_invalid_state(self): 419 # Test when object's trait that sets the invalid state changes, 420 # the invalid state on the editor changes 421 factory = StubEditorFactory(invalid="invalid_state") 422 user_object = UserObject(invalid_state=False) 423 context = { 424 "object": user_object, 425 } 426 editor = create_editor(context=context, factory=factory) 427 editor.prepare(None) 428 self.addCleanup(editor.dispose) 429 430 with self.assertTraitChanges(editor, "invalid", count=1): 431 user_object.invalid_state = True 432 433 self.assertTrue(editor.invalid) 434 435 with self.assertTraitChanges(editor, "invalid", count=1): 436 user_object.invalid_state = False 437 438 self.assertFalse(editor.invalid) 439 440 # Testing sync_value "from" --------------------------------------------- 441 442 def test_sync_value_from(self): 443 editor = create_editor() 444 user_object = editor.object 445 editor.prepare(None) 446 447 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 448 editor.sync_value( 449 "object.user_auxiliary", "auxiliary_value", "from" 450 ) 451 452 self.assertEqual(editor.auxiliary_value, 10) 453 454 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 455 user_object.user_auxiliary = 11 456 457 self.assertEqual(editor.auxiliary_value, 11) 458 459 editor.dispose() 460 461 with self.assertTraitDoesNotChange(editor, "auxiliary_value"): 462 user_object.user_auxiliary = 12 463 464 def test_sync_value_from_object(self): 465 editor = create_editor() 466 user_object = editor.object 467 editor.prepare(None) 468 469 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 470 editor.sync_value("user_auxiliary", "auxiliary_value", "from") 471 472 self.assertEqual(editor.auxiliary_value, 10) 473 474 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 475 user_object.user_auxiliary = 11 476 477 self.assertEqual(editor.auxiliary_value, 11) 478 479 editor.dispose() 480 481 with self.assertTraitDoesNotChange(editor, "auxiliary_value"): 482 user_object.user_auxiliary = 12 483 484 def test_sync_value_from_context(self): 485 # set up the editor 486 user_object = UserObject() 487 other_object = UserObject(user_auxiliary=20) 488 context = {"object": user_object, "other_object": other_object} 489 editor = create_editor(context=context) 490 editor.prepare(None) 491 492 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 493 editor.sync_value( 494 "other_object.user_auxiliary", "auxiliary_value", "from" 495 ) 496 497 self.assertEqual(editor.auxiliary_value, 20) 498 499 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 500 other_object.user_auxiliary = 11 501 502 self.assertEqual(editor.auxiliary_value, 11) 503 504 editor.dispose() 505 506 with self.assertTraitDoesNotChange(editor, "auxiliary_value"): 507 other_object.user_auxiliary = 12 508 509 def test_sync_value_from_chained(self): 510 # set up the editor 511 user_object = UserObject(user_auxiliary=UserObject(user_value=20)) 512 context = {"object": user_object} 513 editor = create_editor(context=context) 514 editor.prepare(None) 515 516 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 517 editor.sync_value( 518 "object.user_auxiliary.user_value", "auxiliary_value", "from" 519 ) 520 521 self.assertEqual(editor.auxiliary_value, 20) 522 523 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 524 user_object.user_auxiliary.user_value = 11 525 526 self.assertEqual(editor.auxiliary_value, 11) 527 528 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 529 user_object.user_auxiliary = UserObject(user_value=12) 530 531 self.assertEqual(editor.auxiliary_value, 12) 532 533 editor.dispose() 534 535 with self.assertTraitDoesNotChange(editor, "auxiliary_value"): 536 user_object.user_auxiliary.user_value = 13 537 538 def test_sync_value_from_list(self): 539 editor = create_editor() 540 user_object = editor.object 541 editor.prepare(None) 542 543 with self.assertTraitChanges(editor, "auxiliary_list", count=1): 544 editor.sync_value( 545 "object.user_list", "auxiliary_list", "from", is_list=True 546 ) 547 548 self.assertEqual(editor.auxiliary_list, ["one", "two", "three"]) 549 550 with self.assertTraitChanges(editor, "auxiliary_list", count=1): 551 user_object.user_list = ["one", "two"] 552 553 self.assertEqual(editor.auxiliary_list, ["one", "two"]) 554 555 with self.assertTraitChanges(editor, "auxiliary_list_items", count=1): 556 user_object.user_list[1:] = ["four", "five"] 557 558 self.assertEqual(editor.auxiliary_list, ["one", "four", "five"]) 559 560 editor.dispose() 561 562 with self.assertTraitDoesNotChange(editor, "auxiliary_list"): 563 user_object.user_list = ["one", "two", "three"] 564 565 def test_sync_value_from_event(self): 566 editor = create_editor() 567 user_object = editor.object 568 editor.prepare(None) 569 570 with self.assertTraitDoesNotChange(editor, "auxiliary_event"): 571 editor.sync_value( 572 "object.user_event", "auxiliary_event", "from", is_event=True 573 ) 574 575 with self.assertTraitChanges(editor, "auxiliary_event", count=1): 576 user_object.user_event = True 577 578 editor.dispose() 579 580 with self.assertTraitDoesNotChange(editor, "auxiliary_event"): 581 user_object.user_event = True 582 583 def test_sync_value_from_cv(self): 584 factory = StubEditorFactory( 585 auxiliary_cv_int=ContextValue("object.user_auxiliary") 586 ) 587 editor = create_editor(factory=factory) 588 user_object = editor.object 589 590 with self.assertTraitChanges(editor, "auxiliary_cv_int", count=1): 591 editor.prepare(None) 592 593 self.assertEqual(editor.auxiliary_cv_int, 10) 594 595 with self.assertTraitChanges(editor, "auxiliary_cv_int", count=1): 596 user_object.user_auxiliary = 11 597 598 self.assertEqual(editor.auxiliary_cv_int, 11) 599 600 editor.dispose() 601 602 with self.assertTraitDoesNotChange(editor, "auxiliary_cv_int"): 603 user_object.user_auxiliary = 12 604 605 # Testing sync_value "to" ----------------------------------------------- 606 607 def test_sync_value_to(self): 608 editor = create_editor() 609 user_object = editor.object 610 editor.prepare(None) 611 editor.auxiliary_value = 20 612 613 with self.assertTraitChanges(user_object, "user_auxiliary", count=1): 614 editor.sync_value("object.user_auxiliary", "auxiliary_value", "to") 615 616 self.assertEqual(user_object.user_auxiliary, 20) 617 618 with self.assertTraitChanges(user_object, "user_auxiliary", count=1): 619 editor.auxiliary_value = 11 620 621 self.assertEqual(user_object.user_auxiliary, 11) 622 623 editor.dispose() 624 625 with self.assertTraitDoesNotChange(user_object, "user_auxiliary"): 626 editor.auxiliary_value = 12 627 628 def test_sync_value_to_object(self): 629 editor = create_editor() 630 user_object = editor.object 631 editor.prepare(None) 632 editor.auxiliary_value = 20 633 634 with self.assertTraitChanges(user_object, "user_auxiliary", count=1): 635 editor.sync_value("user_auxiliary", "auxiliary_value", "to") 636 637 self.assertEqual(user_object.user_auxiliary, 20) 638 639 with self.assertTraitChanges(user_object, "user_auxiliary", count=1): 640 editor.auxiliary_value = 11 641 642 self.assertEqual(user_object.user_auxiliary, 11) 643 644 editor.dispose() 645 646 with self.assertTraitDoesNotChange(user_object, "user_auxiliary"): 647 editor.auxiliary_value = 12 648 649 def test_sync_value_to_context(self): 650 # set up the editor 651 user_object = UserObject() 652 other_object = UserObject() 653 context = {"object": user_object, "other_object": other_object} 654 editor = create_editor(context=context) 655 editor.prepare(None) 656 editor.auxiliary_value = 20 657 658 with self.assertTraitChanges(other_object, "user_auxiliary", count=1): 659 editor.sync_value( 660 "other_object.user_auxiliary", "auxiliary_value", "to" 661 ) 662 663 self.assertEqual(other_object.user_auxiliary, 20) 664 665 with self.assertTraitChanges(other_object, "user_auxiliary", count=1): 666 editor.auxiliary_value = 11 667 668 self.assertEqual(other_object.user_auxiliary, 11) 669 670 editor.dispose() 671 672 with self.assertTraitDoesNotChange(other_object, "user_auxiliary"): 673 editor.auxiliary_value = 12 674 675 def test_sync_value_to_chained(self): 676 user_object = UserObject(user_auxiliary=UserObject()) 677 context = {"object": user_object} 678 editor = create_editor(context=context) 679 editor.prepare(None) 680 editor.auxiliary_value = 20 681 682 with self.assertTraitChanges( 683 user_object.user_auxiliary, "user_value", count=1 684 ): 685 editor.sync_value( 686 "object.user_auxiliary.user_value", "auxiliary_value", "to" 687 ) 688 689 self.assertEqual(user_object.user_auxiliary.user_value, 20) 690 691 with self.assertTraitChanges( 692 user_object.user_auxiliary, "user_value", count=1 693 ): 694 editor.auxiliary_value = 11 695 696 self.assertEqual(user_object.user_auxiliary.user_value, 11) 697 698 editor.dispose() 699 700 with self.assertTraitDoesNotChange( 701 user_object.user_auxiliary, "user_value" 702 ): 703 editor.auxiliary_value = 12 704 705 def test_sync_value_to_list(self): 706 editor = create_editor() 707 user_object = editor.object 708 editor.prepare(None) 709 710 with self.assertTraitChanges(user_object, "user_list", count=1): 711 editor.sync_value( 712 "object.user_list", "auxiliary_list", "to", is_list=True 713 ) 714 715 self.assertEqual(user_object.user_list, []) 716 717 with self.assertTraitChanges(user_object, "user_list", count=1): 718 editor.auxiliary_list = ["one", "two"] 719 720 self.assertEqual(user_object.user_list, ["one", "two"]) 721 722 with self.assertTraitChanges(user_object, "user_list_items", count=1): 723 editor.auxiliary_list[1:] = ["four", "five"] 724 725 self.assertEqual(user_object.user_list, ["one", "four", "five"]) 726 727 editor.dispose() 728 729 with self.assertTraitDoesNotChange(user_object, "user_list"): 730 editor.auxiliary_list = ["one", "two", "three"] 731 732 def test_sync_value_to_event(self): 733 editor = create_editor() 734 user_object = editor.object 735 editor.prepare(None) 736 737 with self.assertTraitDoesNotChange(user_object, "user_event"): 738 editor.sync_value( 739 "object.user_event", "auxiliary_event", "to", is_event=True 740 ) 741 742 with self.assertTraitChanges(user_object, "user_event", count=1): 743 editor.auxiliary_event = True 744 745 editor.dispose() 746 747 with self.assertTraitDoesNotChange(user_object, "user_event"): 748 editor.auxiliary_event = True 749 750 def test_sync_value_to_cv(self): 751 factory = StubEditorFactory( 752 auxiliary_cv_float=ContextValue("object.user_auxiliary") 753 ) 754 editor = create_editor(factory=factory) 755 user_object = editor.object 756 editor.auxiliary_cv_float = 20.0 757 758 with self.assertTraitChanges(user_object, "user_auxiliary", count=1): 759 editor.prepare(None) 760 761 self.assertEqual(user_object.user_auxiliary, 20) 762 763 with self.assertTraitChanges(user_object, "user_auxiliary", count=1): 764 editor.auxiliary_cv_float = 11.0 765 766 self.assertEqual(user_object.user_auxiliary, 11) 767 768 editor.dispose() 769 770 with self.assertTraitDoesNotChange(user_object, "user_auxiliary"): 771 editor.auxiliary_cv_float = 12.0 772 773 # Testing sync_value "both" ----------------------------------------------- 774 775 def test_sync_value_both(self): 776 editor = create_editor() 777 user_object = editor.object 778 editor.prepare(None) 779 780 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 781 editor.sync_value( 782 "object.user_auxiliary", "auxiliary_value", "both" 783 ) 784 785 self.assertEqual(editor.auxiliary_value, 10) 786 787 with self.assertTraitChanges(editor, "auxiliary_value", count=1): 788 user_object.user_auxiliary = 11 789 790 self.assertEqual(editor.auxiliary_value, 11) 791 792 with self.assertTraitChanges(user_object, "user_auxiliary", count=1): 793 editor.auxiliary_value = 12 794 795 self.assertEqual(user_object.user_auxiliary, 12) 796 797 editor.dispose() 798 799 with self.assertTraitDoesNotChange(editor, "auxiliary_value"): 800 user_object.user_auxiliary = 13 801 802 with self.assertTraitDoesNotChange(user_object, "user_auxiliary"): 803 editor.auxiliary_value = 14 804