|
1 | 1 | import asyncio |
2 | 2 | import numpy |
3 | 3 | import os |
| 4 | +import re |
4 | 5 | import pytest |
5 | 6 | from enum import Enum |
6 | 7 |
|
@@ -682,6 +683,95 @@ def test_on_update_true_false(self, out_records): |
682 | 683 | always_update is True and the put'ed value is always different""" |
683 | 684 | self.on_update_runner(out_records, True, False) |
684 | 685 |
|
| 686 | + def on_update_recursive_set_test_func( |
| 687 | + self, device_name, conn |
| 688 | + ): |
| 689 | + log("CHILD: Child started") |
| 690 | + |
| 691 | + builder.SetDeviceName(device_name) |
| 692 | + |
| 693 | + async def on_update_func(new_val): |
| 694 | + log("CHILD: on_update_func started") |
| 695 | + record.set(0, process=False) |
| 696 | + conn.send("C") # "Callback" |
| 697 | + log("CHILD: on_update_func ended") |
| 698 | + |
| 699 | + record = builder.Action( |
| 700 | + "ACTION", |
| 701 | + on_update=on_update_func, |
| 702 | + blocking=True, |
| 703 | + initial_value=1 # A non-zero value, to check it changes |
| 704 | + ) |
| 705 | + |
| 706 | + dispatcher = asyncio_dispatcher.AsyncioDispatcher() |
| 707 | + builder.LoadDatabase() |
| 708 | + softioc.iocInit(dispatcher) |
| 709 | + |
| 710 | + conn.send("R") # "Ready" |
| 711 | + |
| 712 | + log("CHILD: Sent R over Connection to Parent") |
| 713 | + |
| 714 | + # Keep process alive while main thread runs CAGET |
| 715 | + if conn.poll(TIMEOUT): |
| 716 | + val = conn.recv() |
| 717 | + assert val == "D", "Did not receive expected Done character" |
| 718 | + |
| 719 | + log("CHILD: Received exit command, child exiting") |
| 720 | + |
| 721 | + async def test_on_update_recursive_set(self): |
| 722 | + """Test that on_update functions correctly when the on_update |
| 723 | + callback sets the value of the record again (with process=False). |
| 724 | + See issue #201""" |
| 725 | + |
| 726 | + ctx = get_multiprocessing_context() |
| 727 | + parent_conn, child_conn = ctx.Pipe() |
| 728 | + |
| 729 | + device_name = create_random_prefix() |
| 730 | + |
| 731 | + process = ctx.Process( |
| 732 | + target=self.on_update_recursive_set_test_func, |
| 733 | + args=(device_name, child_conn), |
| 734 | + ) |
| 735 | + |
| 736 | + process.start() |
| 737 | + |
| 738 | + log("PARENT: Child started, waiting for R command") |
| 739 | + |
| 740 | + from aioca import caget, caput |
| 741 | + |
| 742 | + try: |
| 743 | + # Wait for message that IOC has started |
| 744 | + select_and_recv(parent_conn, "R") |
| 745 | + |
| 746 | + log("PARENT: received R command") |
| 747 | + |
| 748 | + record = f"{device_name}:ACTION" |
| 749 | + |
| 750 | + val = await caget(record) |
| 751 | + |
| 752 | + assert val == 1, "ACTION record did not start with value 1" |
| 753 | + |
| 754 | + await caput(record, 1, wait=True) |
| 755 | + |
| 756 | + val = await caget(record) |
| 757 | + |
| 758 | + assert val == 0, "ACTION record did not return to zero value" |
| 759 | + |
| 760 | + # Expect one "C" |
| 761 | + select_and_recv(parent_conn, "C") |
| 762 | + |
| 763 | + # ...But if we receive another we know there's a problem |
| 764 | + if parent_conn.poll(5): # Shorter timeout to make this quicker |
| 765 | + pytest.fail("Received unexpected second message") |
| 766 | + |
| 767 | + finally: |
| 768 | + log("PARENT:Sending Done command to child") |
| 769 | + parent_conn.send("D") # "Done" |
| 770 | + process.join(timeout=TIMEOUT) |
| 771 | + log(f"PARENT: Join completed with exitcode {process.exitcode}") |
| 772 | + if process.exitcode is None: |
| 773 | + pytest.fail("Process did not terminate") |
| 774 | + |
685 | 775 |
|
686 | 776 |
|
687 | 777 | class TestBlocking: |
|
0 commit comments