Skip to content

Commit

Permalink
Merge pull request #230 from simonsobs/agent-unit-tests
Browse files Browse the repository at this point in the history
Create core Agent unit tests
  • Loading branch information
BrianJKoopman committed Nov 4, 2021
2 parents fce3eb9 + 6df7581 commit 2a842c6
Show file tree
Hide file tree
Showing 23 changed files with 602 additions and 45 deletions.
11 changes: 2 additions & 9 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
[run]
source = ocs

omit =
# omit versioneer
*/_version.py

[paths]
source =
ocs/
/app/ocs/ocs/
./
/app/ocs/
25 changes: 19 additions & 6 deletions .github/workflows/develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,44 @@ on:

jobs:
build:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
# Fetch all history for all tags and branches
with:
fetch-depth: 0
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: clone ocs
uses: actions/checkout@v2
# Fetch all history for all tags and branches
with:
fetch-depth: 0

- name: Install ocs
run: |
pip3 install -r requirements.txt
pip3 install -e .
# Test (steps from pytest workflow)
- name: Run unit tests
working-directory: ./tests
run: |
COVERAGE_FILE=.coverage.unit python3 -m pytest --cov -m 'not (integtest or spt3g)'
- name: Build docker images
run: |
docker-compose build
- name: Test with pytest within a docker container
run: |
docker run -v $PWD:/coverage --rm ocs sh -c "COVERAGE_FILE=/coverage/.coverage.docker python3 -m pytest -m 'not integtest' --cov /app/ocs/ocs/ ./tests/"
docker run -v $PWD:/coverage --rm -w="/app/ocs/tests/" ocs sh -c "COVERAGE_FILE=/coverage/.coverage.docker python3 -m pytest --cov -m 'spt3g'"
- name: Report test coverage
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mv ./tests/.coverage.* ./
pip install coveralls
coverage combine
coverage report
Expand Down
37 changes: 29 additions & 8 deletions .github/workflows/official-docker-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,45 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
# Fetch all history for all tags and branches
with:
fetch-depth: 0
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8

# Build
- name: clone ocs
uses: actions/checkout@v2
# Fetch all history for all tags and branches
with:
fetch-depth: 0

- name: Install ocs
run: |
pip3 install -r requirements.txt
pip3 install -e .
# Test (already been run by pytest workflow, but they don't take long...)
- name: Run unit tests
working-directory: ./tests
run: |
COVERAGE_FILE=.coverage.unit python3 -m pytest --cov -m 'not (integtest or spt3g)'
- name: Build docker images
run: |
docker-compose build
# Test (already been run by pytest workflow, but they don't take long...)
- name: Test with pytest wtihin a docker container
- name: Test with pytest within a docker container
run: |
docker run -v $PWD:/coverage --rm -w="/app/ocs/tests/" ocs sh -c "COVERAGE_FILE=/coverage/.coverage.docker python3 -m pytest --cov -m 'spt3g'"
- name: Report test coverage
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
docker run -v $PWD:/coverage --rm ocs sh -c "COVERAGE_FILE=/coverage/.coverage.docker python3 -m pytest -m 'not integtest' --cov /app/ocs/ocs/ ./tests/"
mv ./tests/.coverage.* ./
pip install coveralls
coverage combine
coverage report
coveralls --service=github
# Dockerize
- name: Build and push official docker image
Expand Down
28 changes: 21 additions & 7 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,16 @@ on:

jobs:
build:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Build docker images
run: |
docker-compose build
- name: clone ocs
uses: actions/checkout@v2

# I like this idea, but let's hold off for now.
#- name: Lint with flake8
Expand All @@ -31,14 +29,30 @@ jobs:
# # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
# flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

- name: Test with pytest wtihin a docker container
- name: Install ocs
run: |
pip3 install -r requirements.txt
pip3 install -e .
# Unit Tests
- name: Run unit tests
working-directory: ./tests
run: |
COVERAGE_FILE=.coverage.unit python3 -m pytest --cov -m 'not (integtest or spt3g)'
- name: Build docker images
run: |
docker-compose build
- name: Test with pytest within a docker container
run: |
docker run -v $PWD:/coverage --rm ocs sh -c "COVERAGE_FILE=/coverage/.coverage.docker python3 -m pytest -m 'not integtest' --cov /app/ocs/ocs/ ./tests/"
docker run -v $PWD:/coverage --rm -w="/app/ocs/tests/" ocs sh -c "COVERAGE_FILE=/coverage/.coverage.docker python3 -m pytest --cov -m 'spt3g'"
- name: Report test coverage
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mv ./tests/.coverage.* ./
pip install coveralls
coverage combine
coverage report
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ missing some dependencies), first make sure you build the latest ocs image,
then use docker run::

$ docker build -t ocs .
$ docker run --rm ocs sh -c "python3 -m pytest -p no:wampy -m 'not integtest' ./tests/"
$ docker run --rm -w="/app/ocs/tests/" ocs sh -c "python3 -m pytest -m 'not integtest'"

For more details see `tests/README.rst <tests_>`_.

Expand Down
22 changes: 18 additions & 4 deletions agents/aggregator/aggregator_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from os import environ
from ocs import ocs_agent, site_config
from ocs.base import OpCode
from ocs.agent.aggregator import Aggregator

# For logging
Expand Down Expand Up @@ -76,13 +77,18 @@ def _enqueue_incoming_data(self, _data):
self.incoming_data.put((data, feed))
self.log.debug("Enqueued {d} from Feed {f}", d=data, f=feed)

@ocs_agent.param('test_mode', default=False, type=bool)
def record(self, session: ocs_agent.OpSession, params):
"""record()
"""record(test_mode=False)
**Process** - This process will create an Aggregator instance, which
will collect and write provider data to disk as long as this process is
running.
Parameters:
test_mode (bool, optional): Run the record Process loop only once.
This is meant only for testing. Default is False.
Notes:
The most recent file and active providers will be returned in the
session data::
Expand Down Expand Up @@ -123,14 +129,22 @@ def record(self, session: ocs_agent.OpSession, params):
time.sleep(self.loop_time)
aggregator.run()

if params['test_mode']:
break

aggregator.close()

return True, "Aggregation has ended"

def _stop_record(self, session, params):
session.set_status('stopping')
self.aggregate = False
return True, "Stopping aggregation"
if OpCode(session.op_code) in [OpCode.STARTING, OpCode.RUNNING]:
session.set_status('stopping')
self.aggregate = False
return True, "Stopping aggregation"
elif OpCode(session.op_code) == OpCode.STOPPING:
return True, "record process status is already 'stopping'"
else:
return False, "record process not currently running"


def make_parser(parser=None):
Expand Down
11 changes: 9 additions & 2 deletions agents/fake_data/fake_data_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,16 @@ def set_job_done(self):

# Process functions.

@ocs_agent.param('_') # Reject all params.
@ocs_agent.param('test_mode', default=False, type=bool)
def acq(self, session, params):
"""acq()
"""acq(test_mode=False)
**Process** - Acquire data and write to the feed.
Parameters:
test_mode (bool, optional): Run the acq Process loop only once.
This is meant only for testing. Default is False.
Notes:
The most recent fake values are stored in the session data object in
the format::
Expand Down Expand Up @@ -143,6 +147,9 @@ def acq(self, session, params):
data_cache['timestamp'] = block.timestamps[-1]
session.data.update(data_cache)

if params['test_mode']:
break

self.agent.feeds['false_temperatures'].flush_buffer()
self.set_job_done()
return True, 'Acquisition exited cleanly.'
Expand Down
20 changes: 17 additions & 3 deletions agents/influxdb_publisher/influxdb_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from os import environ

from ocs import ocs_agent, site_config
from ocs.base import OpCode
from ocs.agent.influxdb_publisher import Publisher

# For logging
Expand Down Expand Up @@ -71,13 +72,18 @@ def _enqueue_incoming_data(self, _data):

self.incoming_data.put((data, feed))

@ocs_agent.param('test_mode', default=False, type=bool)
def record(self, session: ocs_agent.OpSession, params):
"""record()
**Process** - This process will create an Publisher instance, which
will collect and write provider data to disk as long as this process is
running.
Parameters:
test_mode (bool, optional): Run the record Process loop only once.
This is meant only for testing. Default is False.
"""
session.set_status('starting')
self.aggregate = True
Expand All @@ -97,14 +103,22 @@ def record(self, session: ocs_agent.OpSession, params):
self.log.debug(f"Approx. queue size: {self.incoming_data.qsize()}")
publisher.run()

if params['test_mode']:
break

publisher.close()

return True, "Aggregation has ended"

def _stop_record(self, session, params):
session.set_status('stopping')
self.aggregate = False
return True, "Stopping aggregation"
if OpCode(session.op_code) in [OpCode.STARTING, OpCode.RUNNING]:
session.set_status('stopping')
self.aggregate = False
return True, "Stopping aggregation"
elif OpCode(session.op_code) == OpCode.STOPPING:
return True, "record process status is already 'stopping'"
else:
return False, "record process not currently running"


def make_parser(parser=None):
Expand Down
1 change: 1 addition & 0 deletions agents/ocs_plugin_standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
('AggregatorAgent', 'aggregator/aggregator_agent.py'),
('HostMaster', 'host_master/host_master.py'),
('FakeDataAgent', 'fake_data/fake_data_agent.py'),
('InfluxDBAgent', 'influxdb_publisher/influxdb_publisher.py'),
]:
ocs.site_config.register_agent_class(n, os.path.join(root, f))
21 changes: 18 additions & 3 deletions agents/registry/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ def __init__(self, agent):
self.log = agent.log
self.agent = agent

# Tracking for 'main' Process
self._run = False

# Dict containing agent_data for each registered agent
self.registered_agents = defaultdict(RegisteredAgent)
self.agent_timeout = 5.0 # Removes agent after 5 seconds of no heartbeat.
Expand All @@ -105,15 +108,20 @@ def _register_heartbeat(self, _data):
op_codes, feed = _data
self.registered_agents[feed['agent_address']].refresh(op_codes=op_codes)

@ocs_agent.param('test_mode', default=False, type=bool)
@inlineCallbacks
def main(self, session: ocs_agent.OpSession, params):
"""main()
"""main(test_mode=False)
**Process** - Main run process for the Registry agent. This will loop
and keep track of which agents have expired. It will keep track of
current active agents in the session.data variable so it can be seen by
clients.
Parameters:
test_mode (bool, optional): Run the main Process loop only once.
This is meant only for testing. Default is False.
Notes:
The session data object for this process will be a dictionary containing
the encoded RegisteredAgent objects for each agent observed during the
Expand Down Expand Up @@ -168,12 +176,19 @@ def main(self, session: ocs_agent.OpSession, params):
if msg['data']:
self.agent.publish_to_feed('agent_operations', msg)

if params['test_mode']:
break

return True, "Stopped registry main process"

def _stop_main(self, session, params):
"""Stop function for the 'main' process."""
session.set_status('stopping')
self._run = False
if self._run:
session.set_status('stopping')
self._run = False
return True, 'requested to stop main process'
else:
return False, 'main process not currently running'

def _register_agent(self, session, agent_data):
self.log.warn(
Expand Down
8 changes: 8 additions & 0 deletions tests/.coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[run]
source =
../ocs
../agents/

omit =
# omit versioneer
*/_version.py
2 changes: 1 addition & 1 deletion tests/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Code Coverage
`````````````
Code coverage reports can be produced with the ``--cov`` flag::

python3 -m pytest -m 'not integtest' --cov=ocs
python3 -m pytest -m 'not integtest' --cov

These results are also published to `coveralls`_.

Expand Down

0 comments on commit 2a842c6

Please sign in to comment.