Skip to content

Commit

Permalink
Started building out the interface for custom complex reasoning plug …
Browse files Browse the repository at this point in the history
…ins. Closes #55, this depends on PR #53
  • Loading branch information
Evana Gizzi authored and asgibson committed Oct 25, 2023
1 parent aa27888 commit 0db0d23
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 0 deletions.
Empty file.
41 changes: 41 additions & 0 deletions onair/src/reasoning/reasoning_plugin_abstract/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# GSC-19165-1, "The On-Board Artificial Intelligence Research (OnAIR) Platform"
#
# Copyright © 2023 United States Government as represented by the Administrator of
# the National Aeronautics and Space Administration. No copyright is claimed in the
# United States under Title 17, U.S. Code. All Other Rights Reserved.
#
# Licensed under the NASA Open Source Agreement version 1.3
# See "NOSA GSC-19165-1 OnAIR.pdf"

from abc import ABC, abstractmethod
"""This object serves as a proxy for all complex plug-ins.
Therefore, the ReasoningPlugIn object is meant to induce
standards and structures of compliance for user-created
and/or imported plug-ins/libraries
"""
class ReasoningPlugIn(ABC):
def __init__(self, _name, _headers):
"""
Superclass for custom/complex reasoning plugins that users can write.
Appropriate for instances where intelligence may leverage many forms of
AI plugins in a non-trivial way. Also, allows for easier modularity.
"""
assert(len(_headers)>0)
self.component_name = _name
self.headers = _headers

@abstractmethod
def update(self, frame=[], high_level_info=[]):
"""
Given streamed data point and other sources of high-level info,
system should update internally
"""
raise NotImplementedError

@abstractmethod
def render_reasoning(self):
"""
System should return its diagnosis
"""
raise NotImplementedError

Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# GSC-19165-1, "The On-Board Artificial Intelligence Research (OnAIR) Platform"
#
# Copyright © 2023 United States Government as represented by the Administrator of
# the National Aeronautics and Space Administration. No copyright is claimed in the
# United States under Title 17, U.S. Code. All Other Rights Reserved.
#
# Licensed under the NASA Open Source Agreement version 1.3
# See "NOSA GSC-19165-1 OnAIR.pdf"

""" Test Generic Component Core (abstract class) Functionality """
import pytest
from mock import MagicMock

import onair.src.reasoning.reasoning_plugin_abstract.core as core
from onair.src.reasoning.reasoning_plugin_abstract.core import ReasoningPlugIn

class FakeReasoningPlugIn(ReasoningPlugIn):
def __init__(self, _name, _headers):
return super().__init__(_name, _headers)

def update(self):
return None

def render_reasoning(self):
return dict()

class IncompleteFakeReasoningPlugIn(ReasoningPlugIn):
def __init__(self, _name, _headers):
return super().__init__(_name, _headers)

class BadFakeReasoningPlugIn(ReasoningPlugIn):
def __init__(self, _name, _headers):
return super().__init__(_name, _headers)

def update(self):
return super().update()

def render_reasoning(self):
return super().render_reasoning()

# abstract methods tests
def test_ReasoningPlugIn_raises_error_because_of_unimplemented_abstract_methods():
# Arrange - None
# Act
with pytest.raises(TypeError) as e_info:
cut = ReasoningPlugIn.__new__(ReasoningPlugIn)

# Assert
assert "Can't instantiate abstract class ReasoningPlugIn with" in e_info.__str__()
assert "update" in e_info.__str__()
assert "render_reasoning" in e_info.__str__()

# Incomplete plugin call tests
def test_ReasoningPlugIn_raises_error_when_an_inherited_class_is_instantiated_because_abstract_methods_are_not_implemented_by_that_class():
# Arrange - None
# Act
with pytest.raises(TypeError) as e_info:
cut = IncompleteFakeReasoningPlugIn.__new__(IncompleteFakeReasoningPlugIn)

# Assert
assert "Can't instantiate abstract class IncompleteFakeReasoningPlugIn with" in e_info.__str__()
assert "update" in e_info.__str__()
assert "render_reasoning" in e_info.__str__()

def test_ReasoningPlugIn_raises_error_when_an_inherited_class_calls_abstract_methods_in_parent():
# Act
cut = BadFakeReasoningPlugIn.__new__(BadFakeReasoningPlugIn)

# populate list with the functions that should raise exceptions when called.
not_implemented_functions = [cut.update, cut.render_reasoning]
for fnc in not_implemented_functions:
with pytest.raises(NotImplementedError) as e_info:
fnc()
assert "NotImplementedError" in e_info.__str__()

# Complete plugin call tests
def test_ReasoningPlugIn_does_not_raise_error_when_an_inherited_class_is_instantiated_because_abstract_methods_are_implemented_by_that_class():
# Arrange
exception_raised = False
try:
fake_ic = FakeReasoningPlugIn.__new__(FakeReasoningPlugIn)
except:
exception_raised = True

# Assert
assert exception_raised == False

# Complete plugin call tests

# __init__ tests
def test_ReasoningPlugIn__init__raises_assertion_error_when_given__headers_len_is_not_greater_than_0():
# Arrange
arg__name = MagicMock()
arg__headers = []

cut = FakeReasoningPlugIn.__new__(FakeReasoningPlugIn)

# Act
with pytest.raises(AssertionError) as e_info:
cut.__init__(arg__name, arg__headers)

# Assert
assert e_info.match('')

def test_ReasoningPlugIn__init__sets_instance_values_to_given_args_when_given__headers_len_is_greater_than_0(mocker):
# Arrange
arg__name = MagicMock()
arg__headers = MagicMock()

cut = FakeReasoningPlugIn.__new__(FakeReasoningPlugIn)

mocker.patch(core.__name__ + '.len', return_value=pytest.gen.randint(1, 200)) # arbitrary, from 1 to 200 (but > 0)

# Act
cut.__init__(arg__name, arg__headers)

# Assert
assert core.len.call_count == 1
assert core.len.call_args_list[0].args == (arg__headers,)
assert cut.component_name == arg__name
assert cut.headers == arg__headers

0 comments on commit 0db0d23

Please sign in to comment.