diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 0ab20f1a..df3ad140 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -15,13 +15,28 @@ jobs: runs-on: ${{matrix.os}} strategy: - fail-fast: false + fail-fast: true matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] - os: [ubuntu-latest, macos-latest] # [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.9"] #["3.7", "3.8", "3.9", "3.10"] + os: [windows-latest] # [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v3 + - name: Setup WSL (Windows) + if: startsWith(matrix.os, 'windows') + uses: Vampire/setup-wsl@v2.0.1 + with: + distribution: Ubuntu-22.04 + wsl-shell-command: bash + - name: Setup Python on WSL + if: startsWith(matrix.os, 'windows') + shell: wsl-bash {0} + run: | + pwd + sudo apt-get update -y + sudo apt-get install -y python3-pip + sudo apt-get install -y python3.10-venv + - name: clone repo + uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: @@ -32,16 +47,36 @@ jobs: - name: Install package (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | - pip install -e .[tests,lint] --no-binary pyzmq + pip install -e .[tests,lint] --no-binary pyzmq - name: Install package (Mac) if: startsWith(matrix.os, 'macos') run: | - pip install -e .[tests,lint] + pip install -e .[tests] + - name: Install package (Windows) + if: startsWith(matrix.os, 'windows') + shell: wsl-bash {0} + run: | + pip install --upgrade pip setuptools + pip install build + pip install flake8 pytest + python3 -m build + pip install -e .[tests,lint] --no-binary pyzmq - - name: Test with pytest + - name: Test with pytest (Mac) + if: startsWith(matrix.os, 'macos') run: | - python -m pytest --cov=improv - + # pytest -vv --cov=improv --ignore=test/test_tui.py --ignore=test/test_store_with_errors.py --ignore=test/test_runManager.py --ignore=test/test_link.py --ignore=test/test_nexus.py + - name: Test with pytest (Ubuntu) + if: startsWith(matrix.os, 'ubuntu') + run: | + pytest test/test_actor.py -vv --cov=improv + - name: Test with pytest (Windows) + if: startsWith(matrix.os, 'windows') + shell: wsl-bash {0} + run: | + # PATH=~/.local/bin:$PATH + # pytest test/test_actor.py -vv --cov=improv -k "setStore" -s + pytest test/test_actor.py -vv --cov=improv -k "foo" -s - name: Coveralls uses: coverallsapp/github-action@v2 @@ -67,4 +102,3 @@ jobs: run: | pip install flake8 flake8 - diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e030e568..fae98d1c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -36,4 +36,4 @@ jobs: uses: peaceiris/actions-gh-pages@v3.6.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs/_build/html \ No newline at end of file + publish_dir: docs/_build/html diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 00000000..9568b22b --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,6 @@ +{ + "ExpandedNodes": [ + "" + ], + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 00000000..e69de29b diff --git a/improv/store.py b/improv/store.py index 3a60a6fe..f1e514c6 100644 --- a/improv/store.py +++ b/improv/store.py @@ -102,13 +102,53 @@ def connect_store(self, store_loc): Returns the plasmaclient if successful Updates the client internal """ + + logger.info("attempting to connect to store") + num_attempts = 100 + client = None try: - self.client = plasma.connect(store_loc, 20) - logger.info("Successfully connected to store: {} ".format(store_loc)) - except Exception: - logger.exception("Cannot connect to store: {}".format(store_loc)) - raise CannotConnectToStoreInterfaceError(store_loc) - return self.client + logger.info("beginning connect") + client = plasma.connect(store_loc, num_attempts) + logger.info(client) + logger.info( + "Successfully connected to store at locations {0} ".format(store_loc) + ) + except Exception as e: + logger.warning(e) + logger.warning("Cannot connect to store: {0}".format(store_loc)) + + # for i in range(num_attempts): + # logger.info("starting loop") + # #time.sleep(1) + # #start_time = time.time() + # # delay = 4000; + # # for j in range(delay): #time.sleep() does not work for some reason + # # logger.info(f"delay tick {j}"); + # # logger.info("exited for loop") + + + + # #end_time = time.time() + # #logger.info(f"time: {-1 * start_time + end_time}") + # logger.info("finished sleep") + # try: + # logger.info("beginning connect") + # client = plasma.connect(store_loc, 1) + # logger.info(client) + # logger.info( + # "Successfully connected to store at locations {0} ".format(store_loc) + # ) + # except Exception as e: + # logger.warning(e) + # logger.warning("Cannot connect to store: {0}".format(store_loc)) + # if (i == num_attempts - 1): + # logger.exception("All attempts to connect to the store have failed") + # raise CannotConnectToStoreInterfaceError(store_loc) + # if (client != None): + + # break + + return client def put(self, object, object_name): """ diff --git a/pyproject.toml b/pyproject.toml index 2ca7f9ce..e6638df8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,10 @@ exclude = ["test", "pytest", "env", "demos", "figures"] [tool.pytest.ini_options] asyncio_mode = "auto" filterwarnings = [ ] +log_cli = true +log_cli_level = "INFO" +log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)" +log_cli_date_format = "%Y-%m-%d %H:%M:%S" [tool.flake8] # Recommend matching the black line length (default 88), @@ -68,4 +72,4 @@ exclude = ''' demos | build )/ -''' \ No newline at end of file +''' diff --git a/test/actors/tester.py b/test/actors/tester.py new file mode 100644 index 00000000..fd452ec6 --- /dev/null +++ b/test/actors/tester.py @@ -0,0 +1,31 @@ +from improv.actor import Actor, RunManager +import numpy as np +import logging; logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +class Tester(Actor): + """ Actor intended for use within a testing environment. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.isSetup = False + self.hasRun = False + self.hasStopped = False + self.runNum = 0 + + def setup(self): + self.isSetup = True + + def run(self): + self.hasRun = True + with RunManager('tester', self.runMethod, self.setup, self.q_sig, self.q_comm, self.stop) as rm: + logger.info(rm) + + def runMethod(self): + self.runNum += 1 + + def stop(self): + self.hasStopped = True + + \ No newline at end of file diff --git a/test/store_connect.py b/test/store_connect.py new file mode 100644 index 00000000..d07e09ab --- /dev/null +++ b/test/store_connect.py @@ -0,0 +1,21 @@ +import pyarrow.plasma as plasma + +# start the store + +client = plasma.connect("/tmp/plasma") + +object_id = list() + +object_id.append(client.put("input 0")) +object_id.append(client.put("input 1")) +object_id.append(client.put("input 2")) +object_id.append(client.put("input 3")) + +print(client.get(object_id[3])) +print(client.get(object_id[2])) +print(client.get(object_id[1])) +print(client.get(object_id[0])) + +print("\n") + +print(client.list()) diff --git a/test/test_actor.py b/test/test_actor.py index 0745aa69..e4978635 100644 --- a/test/test_actor.py +++ b/test/test_actor.py @@ -2,6 +2,7 @@ import psutil import pytest import subprocess +import logging from improv.link import Link # , AsyncQueue from improv.actor import AbstractActor as Actor from improv.store import StoreInterface @@ -9,21 +10,28 @@ # set global_variables +LOGGER = logging.getLogger(__name__) + pytest.example_string_links = {} pytest.example_links = {} - @pytest.fixture() def setup_store(set_store_loc, scope="module"): """Fixture to set up the store subprocess with 10 mb.""" + #print(f"set store loc: {set_store_loc}") + LOGGER.info(f"set store loc: {set_store_loc}") p = subprocess.Popen( ["plasma_store", "-s", set_store_loc, "-m", str(10000000)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) yield p + + print("about to wait: first time") + print("about to kill") p.kill() - p.wait() + print("about to wait") + p.wait(10) @pytest.fixture() @@ -60,6 +68,10 @@ def example_links(setup_store, set_store_loc): return pytest.example_links +def test_CI_debug(): + """ For CI debug only; delete afterwards.""" + assert True + @pytest.mark.parametrize( ("attribute", "expected"), [ @@ -79,11 +91,12 @@ def test_default_init(attribute, expected, init_actor): assert atr == expected -def test_repr_default_initialization(init_actor): +def test_repr_default_initialization(init_actor, set_store_loc): """Test if the actor representation has the right dict keys.""" act = init_actor rep = act.__repr__() + #print(f"\nstore_loc: {set_store_loc}\n") assert rep == "Test: dict_keys([])" @@ -92,16 +105,31 @@ def test_repr(example_string_links, set_store_loc): act = Actor("Test", set_store_loc) act.setLinks(example_string_links) + #print(f"set store loc: {set_store_loc}") assert act.__repr__() == "Test: dict_keys(['1', '2', '3'])" def test_setStoreInterface(setup_store, set_store_loc): """Tests if the store is started and linked with the actor.""" - + + #print("HERE"); + LOGGER.info("here") act = Actor("Acquirer", set_store_loc) + #print(f"store_loc: {set_store_loc}") + LOGGER.info(f"store_loc: {set_store_loc}") store = StoreInterface(store_loc=set_store_loc) + print("got store interface") + print("about to connect actor") act.setStoreInterface(store.client) assert act.client is store.client + print("all good!") + +from pyarrow import plasma +def test_foo(setup_store, set_store_loc): + # act = Actor("Acquirer", set_store_loc) + LOGGER.info(f"store_loc: {set_store_loc}") + store = StoreInterface(store_loc=set_store_loc) + store.release() @pytest.mark.parametrize( @@ -114,7 +142,6 @@ def test_setLinks(links, set_store_loc): act.setLinks(links) assert act.links == links - @pytest.mark.parametrize( ("qc", "qs"), [ @@ -305,6 +332,7 @@ def test_actor_connection(setup_store, set_store_loc): StoreInterface(store_loc=set_store_loc) link = Link("L12", act1, act2) + act1.setLinkIn(link) act2.setLinkOut(link) @@ -313,3 +341,125 @@ def test_actor_connection(setup_store, set_store_loc): act1.q_in.put(msg) assert act2.q_out.get() == msg + + +#========================================= +# +# +# +# +# MANAGED ACTOR TESTS +# +# +# +# +#========================================= +@pytest.fixture +def init_managed_actor(init_actor): + """ Fixture to create a managed actor. + """ + mactor = ManagedActor(init_actor) + yield mactor + mactor = None + + +@pytest.fixture +def init_custom_managed_actor(): + """ Fixture to create and yield a managed actor that has been predefined. + """ + pass + #TODO: Implement this + + +#---------- DEFAULT -----------# + +@pytest.mark.skip(reason="unfinished") +def test_default_ManagedActor_init(init_actor): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_default_ManagedActor_setup(init_actor): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_default_ManagedActor_run(init_actor): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_default_ManagedActor_run_step(init_actor): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_default_ManagedActor_stop(init_actor): + pass + +#---------- CUSTOM -----------# + +@pytest.mark.skip(reason="unfinished") +def test_custom_ManagedActor_init(): + + pass + + +@pytest.mark.skip(reason="unfinished") +def test_custom_ManagedActor_setup(): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_custom_ManagedActor_run(): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_custom_ManagedActor_run_step(): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_custom_ManagedActor_stop(): + pass + +#========================================= +# +# +# +# +# ASYNC ACTOR TESTS +# +# +# +# +#========================================= + +@pytest.mark.skip(reason="unfinished") +def test_AsyncActor_init(): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_AsyncActor_run(): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_AsyncActor_setup(): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_AsyncActor_run_step(): + pass + + +@pytest.mark.skip(reason="unfinished") +def test_AsyncActor_stop(): + pass + + + + diff --git a/test/test_runManager.py b/test/test_runManager.py new file mode 100644 index 00000000..38d65940 --- /dev/null +++ b/test/test_runManager.py @@ -0,0 +1,96 @@ +import time +import os +import psutil +import pytest +import subprocess +from improv.link import Link, AsyncQueue +from improv.actor import AbstractActor +from improv.actor import RunManager +from improv.actor import Signal +from improv.store import StoreInterface +from actors.sample_generator import Generator +from actors.sample_processor import Processor + + + + +@pytest.fixture +def init_rm(): + samp_gen = Generator("Test") + samp_proc = Processor("Test") + link_dict = { + 'q_sig': Link("q_sig", samp_gen, samp_proc), + 'q_comm': Link("q_comm", samp_gen, samp_proc), + 'q_in': Link("q_in", samp_gen, samp_proc), + 'q_out': Link("q_out", samp_gen, samp_proc) + } + samp_gen.setLinks(link_dict) + RM = RunManager("Test", samp_gen.runStep, samp_gen.links) + yield [RM, samp_gen, samp_proc] + +@pytest.mark.skip(reason="unfinished") +def test_RM_init(init_rm): + [RM, gen, proc] = init_rm + + assert RM.run == False + assert RM.config == False + assert RM.actorName == "Test" + assert RM.links is gen.links + assert RM.timeout == 1e-6 + + + +@pytest.mark.skip(reason="unfinished") +def test_RM_run(init_rm): + [RM, gen, proc] = init_rm + + #what to assert here? + #assert + + + RM.q_sig.put(Signal.setup()) + for i in range(100): + RM.q_sig.put(Signal.run()) + RM.q_sig.put(Signal.stop()) + RM.q_sig.put(Signal.quit()) + with RM: + print(RM) + assert RM.run == False + assert RM.stop == False + +@pytest.mark.skip(reason="unfinished") +def test_RM_stop_after_run(init_rm): + with init_rm as RM: + logger.info(rm) + + rm.q_sig_in.append("stop") + assert True #how to assert that this RM has actuallly stopped? + +@pytest.mark.skip(reason="unfinished") +def test_RM_config(init_rm): + pass + +@pytest.mark.skip(reason="unfinished") +def test_RM_stop_after_config(init_rm): + pass + +@pytest.mark.skip(reason="unfinished") +def test_RM_signals(init_rm): + + with init_rm as RM: + logger.info(RM) + + RM.q_sig_in.append("setup") + assert True + RM.q_sig_in.append("run") + assert True + RM.q_sig_in.append("stop") + assert True + RM.q_sig_in.append("quit") + assert True + +@pytest.mark.skip(reason="unfinished") +def test_RM_exit(init_rm): + pass + +