|
17 | 17 | from datalab.adapters_metadata.common import ResultData |
18 | 18 | from datalab.gui import historytools_ops as htools |
19 | 19 | from datalab.gui.panel.history import HistoryAction |
| 20 | +from datalab.gui.panel.history import chain as hchain |
20 | 21 | from datalab.gui.panel.history import interactive_replay as hireplay |
21 | 22 | from datalab.gui.panel.history import recompute as hrec |
22 | 23 | from datalab.gui.panel.history.chainmodel import ( |
|
36 | 37 | from datalab.tests.features.common.history_test_helpers import ( |
37 | 38 | add_paracetamol_signals, |
38 | 39 | build_signal_chain, |
| 40 | + get_tree_item, |
39 | 41 | read_history_sessions, |
40 | 42 | select_tree_entry, |
41 | 43 | select_tree_session, |
@@ -493,6 +495,87 @@ def test_multi_action_edit_recomputes_selected_descendants_once() -> None: |
493 | 495 | assert all(action.is_stale is False for action in expected) |
494 | 496 |
|
495 | 497 |
|
| 498 | +def test_edit_mode_selected_session_uses_global_replay_planner() -> None: |
| 499 | + """Plan a selected session and duplicate stale action exactly once.""" |
| 500 | + with datalab_test_app_context(history=True) as win: |
| 501 | + history, panel = win.historypanel, win.signalpanel |
| 502 | + history.toggle_record_mode(True) |
| 503 | + history.toggle_edit_mode(True) |
| 504 | + build_signal_chain(panel, history) |
| 505 | + session = history.history_sessions[-1] |
| 506 | + expected = list(session.actions) |
| 507 | + stale_action = expected[1] |
| 508 | + stale_action.is_stale = True |
| 509 | + select_tree_session(history, session) |
| 510 | + get_tree_item(history, stale_action.uuid).setSelected(True) |
| 511 | + selected = history.tree.get_selected_actions_or_sessions( |
| 512 | + history.history_sessions |
| 513 | + ) |
| 514 | + assert selected == [session, stale_action] |
| 515 | + |
| 516 | + with ( |
| 517 | + patch.object(type(session), "replay") as direct_replay, |
| 518 | + patch.object(hrec, "recompute_cascade") as direct_cascade, |
| 519 | + patch.object( |
| 520 | + hireplay, |
| 521 | + "edit_mode_replay_actions", |
| 522 | + wraps=hireplay.edit_mode_replay_actions, |
| 523 | + ) as edit_planner, |
| 524 | + patch.object( |
| 525 | + hireplay, "prompt_edit_action_params", return_value=True |
| 526 | + ) as prompt, |
| 527 | + patch.object( |
| 528 | + hrec, "recompute_action_in_place", return_value=True |
| 529 | + ) as recompute, |
| 530 | + ): |
| 531 | + hireplay.replay_restore_actions(history) |
| 532 | + |
| 533 | + direct_replay.assert_not_called() |
| 534 | + direct_cascade.assert_not_called() |
| 535 | + edit_planner.assert_called_once_with(history, [*expected, stale_action]) |
| 536 | + assert [call.args[1] for call in prompt.call_args_list] == expected |
| 537 | + assert [call.args[1] for call in recompute.call_args_list] == expected |
| 538 | + assert all(action.is_stale is False for action in expected) |
| 539 | + |
| 540 | + |
| 541 | +def test_downstream_actions_follow_every_registered_output() -> None: |
| 542 | + """Follow second registered outputs through transitive dependencies.""" |
| 543 | + with datalab_test_app_context(history=True) as win: |
| 544 | + history, panel = win.historypanel, win.signalpanel |
| 545 | + history.toggle_record_mode(True) |
| 546 | + producer, consumer, descendant = build_signal_chain(panel, history).actions |
| 547 | + producer_second_output = "producer-second-output" |
| 548 | + consumer_second_output = "consumer-second-output" |
| 549 | + producer.output_uuids.append(producer_second_output) |
| 550 | + consumer.output_uuids.append(consumer_second_output) |
| 551 | + history.runtime.objects.action_output_uuids[producer.uuid] = list( |
| 552 | + producer.output_uuids |
| 553 | + ) |
| 554 | + history.runtime.objects.action_output_uuids[consumer.uuid] = list( |
| 555 | + consumer.output_uuids |
| 556 | + ) |
| 557 | + history.runtime.objects.output_to_action[producer_second_output] = producer.uuid |
| 558 | + history.runtime.objects.output_to_action[consumer_second_output] = consumer.uuid |
| 559 | + hchain.prune_output_mapping(history) |
| 560 | + assert producer_second_output in producer.output_uuids |
| 561 | + assert consumer_second_output in consumer.output_uuids |
| 562 | + assert ( |
| 563 | + producer_second_output |
| 564 | + not in (history.runtime.objects.action_output_uuids[producer.uuid]) |
| 565 | + ) |
| 566 | + assert ( |
| 567 | + consumer_second_output |
| 568 | + not in (history.runtime.objects.action_output_uuids[consumer.uuid]) |
| 569 | + ) |
| 570 | + consumer.state.selection["signal"] = [producer_second_output] |
| 571 | + descendant.state.selection["signal"] = [consumer_second_output] |
| 572 | + |
| 573 | + assert hchain.get_downstream_actions(history, producer) == [ |
| 574 | + consumer, |
| 575 | + descendant, |
| 576 | + ] |
| 577 | + |
| 578 | + |
496 | 579 | def test_multi_action_edit_cascades_across_independent_sessions() -> None: |
497 | 580 | """Recompute edited branches from multiple sessions in global order.""" |
498 | 581 | with datalab_test_app_context(history=True) as win: |
@@ -527,6 +610,20 @@ def test_multi_action_edit_failure_skips_dependents_and_continues() -> None: |
527 | 610 | history.create_new_session(panel_str="signal") |
528 | 611 | successful_chain = build_independent_signal_branch(panel, history) |
529 | 612 | failed_root = failed_chain[0] |
| 613 | + failed_output_uuid = failed_root.output_uuids[0] |
| 614 | + failed_root.output_uuids.clear() |
| 615 | + history.runtime.objects.action_output_uuids.pop(failed_root.uuid) |
| 616 | + history.runtime.objects.output_to_action.pop(failed_output_uuid) |
| 617 | + failed_output = panel.objmodel[failed_output_uuid] |
| 618 | + processing_parameters = extract_processing_parameters(failed_output) |
| 619 | + assert not failed_root.output_uuids |
| 620 | + assert failed_root.uuid not in history.runtime.objects.action_output_uuids |
| 621 | + assert failed_output_uuid not in history.runtime.objects.output_to_action |
| 622 | + assert processing_parameters is not None |
| 623 | + assert processing_parameters.func_name == failed_root.func_name |
| 624 | + assert hchain.recorded_action_output_uuids(history, failed_root) == [ |
| 625 | + failed_output_uuid |
| 626 | + ] |
530 | 627 | recomputed: list[HistoryAction] = [] |
531 | 628 |
|
532 | 629 | def recompute_action(_panel, action): |
|
0 commit comments