-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Started building out the interface for custom complex reasoning plug …
- Loading branch information
Evana Gizzi
authored and
Evana Gizzi
committed
Oct 11, 2023
1 parent
aa27888
commit 044fad5
Showing
4 changed files
with
162 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
File renamed without changes.
121 changes: 121 additions & 0 deletions
121
test/onair/src/reasoning/reasoning_plugin_abstract/test_reasoning_plugin_core.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |