Skip to content

Commit

Permalink
Needed to add a test_planner_interface suite
Browse files Browse the repository at this point in the history
  • Loading branch information
Evana Gizzi committed Oct 6, 2023
1 parent 26e6de8 commit ced65ec
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 9 deletions.
14 changes: 7 additions & 7 deletions onair/src/ai_components/planners_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,26 @@ class PlannersInterface:
def __init__(self, headers, _ai_plugins={}):
assert(len(headers)>0), 'Headers are required'
self.headers = headers
self.planning_constructs = []
for module_name in list(_planning_plugins.keys()):
spec = importlib.util.spec_from_file_location(module_name, _planning_plugins[module_name])
self.ai_constructs = []
for module_name in list(_ai_plugins.keys()):
spec = importlib.util.spec_from_file_location(module_name, _ai_plugins[module_name])
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
self.planning_constructs.append(module.Plugin(module_name,headers))
self.ai_constructs.append(module.Plugin(module_name,headers))

def update(self, curr_data, status):
input_data = curr_data
output_data = status_to_oneHot(status)
for plugin in self.planning_constructs:
for plugin in self.ai_constructs:
plugin.update(input_data)

def apriori_training(self, batch_data):
for plugin in self.planning_constructs:
for plugin in self.ai_constructs:
plugin.apriori_training(batch_data)

def render_reasoning(self):
diagnoses = {}
for plugin in self.planning_constructs:
for plugin in self.ai_constructs:
diagnoses[plugin.component_name] = plugin.render_reasoning()
return diagnoses

Expand Down
7 changes: 6 additions & 1 deletion onair/src/reasoning/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,25 @@
Deals with supervised learning for diagnosing statuses
"""
from ..ai_components.learners_interface import LearnersInterface
from ..ai_components.planners_interface import PlannersInterface
from ..reasoning.diagnosis import Diagnosis

class Agent:
def __init__(self, vehicle, plugin_list):
self.vehicle_rep = vehicle
self.learning_systems = LearnersInterface(self.vehicle_rep.get_headers(),plugin_list)
self.mission_status = self.vehicle_rep.get_status()
self.bayesian_status = self.vehicle_rep.get_bayesian_status()

# AI Interfaces
self.learning_systems = LearnersInterface(self.vehicle_rep.get_headers(),plugin_list)
self.planning_systems = PlannersInterface(self.vehicle_rep.get_headers(),plugin_list)

# Markov Assumption holds
def reason(self, frame):
self.vehicle_rep.update(frame)
self.mission_status = self.vehicle_rep.get_status()
self.learning_systems.update(frame, self.mission_status)
self.planning_systems.update(frame, self.mission_status)

def diagnose(self, time_step):
""" Grab the mnemonics from the """
Expand Down
214 changes: 214 additions & 0 deletions test/onair/src/ai_components/test_planners_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# 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 PlannersInterface Functionality """
import pytest
from mock import MagicMock

import onair.src.ai_components.planners_interface as planners_interface
from onair.src.ai_components.planners_interface import PlannersInterface

import importlib
from typing import Dict

# __init__ tests
def test_PlannersInterface__init__sets_instance_headers_to_given_headers_and_does_nothing_else_when_given__ai_plugins_is_empty(mocker):
# Arrange
arg_headers = []
arg__ai_plugins = {}

num_fake_headers = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 headers (0 has own test)
for i in range(num_fake_headers):
arg_headers.append(MagicMock())

cut = PlannersInterface.__new__(PlannersInterface)

# Act
cut.__init__(arg_headers, arg__ai_plugins)

# Assert
assert cut.headers == arg_headers

def test_PlannersInterface__init__throws_AttributeError_when_given_module_file_has_no_attribute_Plugin(mocker):
# Arrange
fake_module_name = MagicMock()
arg_headers = []

arg__ai_plugins = {MagicMock()}

# Act

# Assert

def test_PlannersInterfacee__init__sets_instance_ai_constructs_to_a_list_of_the_calls_AIPlugIn_with_plugin_and_given_headers_for_each_item_in_given__ai_plugins_when_given__ai_plugins_is_occupied(mocker):
# Arrange
arg_headers = []
arg__ai_plugins = {}
fake_spec_list = []
fake_module_list = []

num_fake_headers = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 headers (0 has own test)
for i in range(num_fake_headers):
arg_headers.append(MagicMock())
num_fake_ai_plugins = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_ai_plugins):
arg__ai_plugins[str(MagicMock())] = str(MagicMock())
fake_spec_list.append(MagicMock())
fake_module_list.append(MagicMock())


expected_ai_constructs = []
for i in range(num_fake_ai_plugins):
expected_ai_constructs.append(MagicMock())

# mocker.patch('importlib.import_module', return_value=fake_imported_module)
mocker.patch('importlib.util.spec_from_file_location',side_effect=fake_spec_list)
mocker.patch('importlib.util.module_from_spec',side_effect=fake_module_list)
for spec in fake_spec_list:
mocker.patch.object(spec,'loader.exec_module')
for i, module in enumerate(fake_module_list):
mocker.patch.object(module,'Plugin',return_value=expected_ai_constructs[i])

cut = PlannersInterface.__new__(PlannersInterface)

# Act
cut.__init__(arg_headers, arg__ai_plugins)

# Assert
assert importlib.util.spec_from_file_location.call_count == len(arg__ai_plugins)
assert importlib.util.module_from_spec.call_count == len(fake_spec_list)

for i in range(num_fake_ai_plugins):
fake_name = list(arg__ai_plugins.keys())[i]
fake_path = arg__ai_plugins[fake_name]
assert importlib.util.spec_from_file_location.call_args_list[i].args == (fake_name,fake_path)
assert importlib.util.module_from_spec.call_args_list[i].args == (fake_spec_list[i],)
assert fake_spec_list[i].loader.exec_module.call_count == 1
assert fake_spec_list[i].loader.exec_module.call_args_list[0].args == (fake_module_list[i],)
assert fake_module_list[i].Plugin.call_count == 1
assert fake_module_list[i].Plugin.call_args_list[0].args == (fake_name,arg_headers)

assert cut.ai_constructs == expected_ai_constructs

# update tests
def test_PlannersInterface_update_only_calls_flotify_input_with_given_curr_data_and_status_to_oneHot_with_given_status_when_instance_ai_constructs_is_empty(mocker):
# Arrange
arg_curr_data = MagicMock()
arg_status = MagicMock()

mocker.patch(planners_interface.__name__ + '.status_to_oneHot')

cut = PlannersInterface.__new__(PlannersInterface)
cut.ai_constructs = []

# Act
result = cut.update(arg_curr_data, arg_status)

# Assert
assert planners_interface.status_to_oneHot.call_count == 1
assert planners_interface.status_to_oneHot.call_args_list[0].args == (arg_status,)
assert result == None

def test_PlannersInterface_update_calls_flotify_input_with_given_curr_data_and_status_to_oneHot_with_given_status_and_calls_update_on_each_ai_construct_with_input_data_when_instance_ai_constructs_is_occupied(mocker):
# Arrange
arg_curr_data = MagicMock()
arg_status = MagicMock()

mocker.patch(planners_interface.__name__ + '.status_to_oneHot')

cut = PlannersInterface.__new__(PlannersInterface)
cut.ai_constructs = []

num_fake_ai_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_ai_constructs):
fake_ai_construct = MagicMock()
cut.ai_constructs.append(fake_ai_construct)
mocker.patch.object(fake_ai_construct, 'update')

# Act
result = cut.update(arg_curr_data, arg_status)

# Assert
assert planners_interface.status_to_oneHot.call_count == 1
assert planners_interface.status_to_oneHot.call_args_list[0].args == (arg_status,)
for i in range(num_fake_ai_constructs):
assert cut.ai_constructs[i].update.call_count == 1
assert cut.ai_constructs[i].update.call_args_list[0].args == (arg_curr_data,)
assert result == None

# apriori_training tests
def test_PlannersInterface_apriori_training_does_nothing_when_instance_ai_constructs_is_empty():
# Arrange
arg_batch_data = MagicMock()

cut = PlannersInterface.__new__(PlannersInterface)
cut.ai_constructs = []

# Act
result = cut.apriori_training(arg_batch_data)

# Assert
assert result == None

def test_PlannersInterface_apriori_training_calls_apriori_training_on_each_ai_constructs_item(mocker):
# Arrange
arg_batch_data = MagicMock()

cut = PlannersInterface.__new__(PlannersInterface)
cut.ai_constructs = []

num_fake_ai_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_ai_constructs):
cut.ai_constructs.append(MagicMock())

# Act
result = cut.apriori_training(arg_batch_data)

# Assert
for i in range(num_fake_ai_constructs):
assert cut.ai_constructs[i].apriori_training.call_count == 1
assert cut.ai_constructs[i].apriori_training.call_args_list[0].args == (arg_batch_data, )
assert result == None

# render_reasoning tests
def test_PlannersInterface_render_reasoning_returns_empty_dict_when_instance_ai_constructs_is_empty(mocker):
# Arrange
cut = PlannersInterface.__new__(PlannersInterface)
cut.ai_constructs = []

# Act
result = cut.render_reasoning()

# Assert
assert result == {}

def test_PlannersInterface_render_reasoning_returns_dict_of_each_ai_construct_as_key_to_the_result_of_its_render_reasoning_when_instance_ai_constructs_is_occupied(mocker):
# Arrange
cut = PlannersInterface.__new__(PlannersInterface)
cut.ai_constructs = []

expected_result = {}

num_fake_ai_constructs = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test)
for i in range(num_fake_ai_constructs):
fake_ai_construct = MagicMock()
forced_return_ai_construct_render_reasoning = MagicMock()
cut.ai_constructs.append(fake_ai_construct)
mocker.patch.object(fake_ai_construct, 'render_reasoning', return_value=forced_return_ai_construct_render_reasoning)
fake_ai_construct.component_name = MagicMock()
expected_result[fake_ai_construct.component_name] = forced_return_ai_construct_render_reasoning

# Act
result = cut.render_reasoning()

# Assert
for i in range(num_fake_ai_constructs):
assert cut.ai_constructs[i].render_reasoning.call_count == 1
assert cut.ai_constructs[i].render_reasoning.call_args_list[0].args == ()
assert result == expected_result
11 changes: 10 additions & 1 deletion test/onair/src/reasoning/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ def test_Agent__init__sets_vehicle_rep_to_given_vehicle_and_learning_systems_and

fake_headers = MagicMock()
fake_learning_systems = MagicMock()
fake_planning_systems = MagicMock()
fake_mission_status = MagicMock()
fake_bayesian_status = MagicMock()
fake_plugin_list = MagicMock()

mocker.patch.object(arg_vehicle, 'get_headers', return_value=fake_headers)
mocker.patch(agent.__name__ + '.LearnersInterface', return_value=fake_learning_systems)
mocker.patch(agent.__name__ + '.PlannersInterface', return_value=fake_planning_systems)
mocker.patch.object(arg_vehicle, 'get_status', return_value=fake_mission_status)
mocker.patch.object(arg_vehicle, 'get_bayesian_status', return_value=fake_bayesian_status)

Expand All @@ -37,11 +39,14 @@ def test_Agent__init__sets_vehicle_rep_to_given_vehicle_and_learning_systems_and

# Assert
assert cut.vehicle_rep == arg_vehicle
assert arg_vehicle.get_headers.call_count == 1
assert arg_vehicle.get_headers.call_count == 2
assert arg_vehicle.get_headers.call_args_list[0].args == ()
assert agent.LearnersInterface.call_count == 1
assert agent.LearnersInterface.call_args_list[0].args == (fake_headers, fake_plugin_list)
assert cut.learning_systems == fake_learning_systems
assert agent.PlannersInterface.call_count == 1
assert agent.PlannersInterface.call_args_list[0].args == (fake_headers, fake_plugin_list)
assert cut.planning_systems == fake_planning_systems
assert arg_vehicle.get_status.call_count == 1
assert arg_vehicle.get_status.call_args_list[0].args == ()
assert cut.mission_status == fake_mission_status
Expand All @@ -59,6 +64,7 @@ def test_Agent_reason_updates_vehicle_rep_with_given_frame_and_sets_new_vehicle_
cut = Agent.__new__(Agent)
cut.vehicle_rep = MagicMock()
cut.learning_systems = MagicMock()
cut.planning_systems = MagicMock()

mocker.patch.object(cut.vehicle_rep, 'update')
mocker.patch.object(cut.vehicle_rep, 'get_status', return_value=fake_mission_status)
Expand All @@ -74,6 +80,8 @@ def test_Agent_reason_updates_vehicle_rep_with_given_frame_and_sets_new_vehicle_
assert cut.vehicle_rep.get_status.call_args_list[0].args == ()
assert cut.learning_systems.update.call_count == 1
assert cut.learning_systems.update.call_args_list[0].args == (arg_frame, fake_mission_status)
assert cut.planning_systems.update.call_count == 1
assert cut.planning_systems.update.call_args_list[0].args == (arg_frame, fake_mission_status)

# diagnose tests
def test_Agent_diagnose_returns_empty_Dict():
Expand All @@ -82,6 +90,7 @@ def test_Agent_diagnose_returns_empty_Dict():

cut = Agent.__new__(Agent)
cut.learning_systems = MagicMock()
cut.planning_systems = MagicMock()
cut.bayesian_status = MagicMock()
cut.vehicle_rep = MagicMock()

Expand Down

0 comments on commit ced65ec

Please sign in to comment.