From 6381320c3f74829cb020e487a2a461ccac458dfe Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Thu, 21 Sep 2023 09:55:27 -0400 Subject: [PATCH 1/9] Rough combining of data source and parser --- onair/data_handling/data_source.py | 1 + onair/data_handling/parsers/csv_parser.py | 41 ++++++++++++------- ...on_air_parser.py => on_air_data_source.py} | 35 +++++++++++++--- .../data_handling/parsers/tlm_json_parser.py | 1 + onair/src/run_scripts/execution_engine.py | 5 +-- onair/src/run_scripts/sbn_adapter.py | 2 +- onair/src/run_scripts/sim.py | 11 +++-- onair/src/systems/vehicle_rep.py | 2 +- .../data_handling/parsers/test_csv_parser.py | 6 +-- ...r_parser.py => test_on_air_data_source.py} | 34 +++++++-------- 10 files changed, 89 insertions(+), 49 deletions(-) rename onair/data_handling/parsers/{on_air_parser.py => on_air_data_source.py} (70%) rename test/onair/data_handling/parsers/{test_on_air_parser.py => test_on_air_data_source.py} (71%) diff --git a/onair/data_handling/data_source.py b/onair/data_handling/data_source.py index afb6b9d7..7ca8e265 100644 --- a/onair/data_handling/data_source.py +++ b/onair/data_handling/data_source.py @@ -23,6 +23,7 @@ def __init__(self, data=[]): # Get the data at self.index and increment the index def get_next(self): + #print("OH NO") self.index = self.index + 1 return self.data[self.index - 1] diff --git a/onair/data_handling/parsers/csv_parser.py b/onair/data_handling/parsers/csv_parser.py index fe33598f..c918b847 100644 --- a/onair/data_handling/parsers/csv_parser.py +++ b/onair/data_handling/parsers/csv_parser.py @@ -14,15 +14,15 @@ import os import pandas as pd -from .on_air_parser import OnAirParser +from .on_air_data_source import OnAirDataSource from ...src.util.print_io import * from .parser_util import * -class CSV(OnAirParser): +class CSV(OnAirDataSource): + def process_data_file(self, data_file): - labels, data = self.parse_csv_data(data_file) - self.all_headers = labels - self.sim_data = data + self.sim_data = self.parse_csv_data(data_file) + self.frame_index = 0 ##### INITIAL PROCESSING #### def parse_csv_data(self, data_file): @@ -30,10 +30,7 @@ def parse_csv_data(self, data_file): dataset = pd.read_csv(data_file, delimiter=',', header=0, dtype=str) dataset = dataset.loc[:, ~dataset.columns.str.contains('^Unnamed')] - all_headers = list(dataset.columns.values) - #Find the 'Time' header in the list in order to match 42 file formatting - # Converting - upperCaseStringHeaders = [x.upper().strip() for x in all_headers if isinstance(x, str)] + # all_headers = list(dataset.columns.values) #Initialize the entire data dictionary all_data = [] @@ -41,14 +38,14 @@ def parse_csv_data(self, data_file): rowVals = floatify_input(list(row)) all_data.append(floatify_input(list(row))) - return all_headers, all_data + return all_data def parse_meta_data_file(self, meta_data_file, ss_breakdown): - parsed_configs = extract_meta_data(meta_data_file) + parsed_meta_data = extract_meta_data(meta_data_file) if ss_breakdown == False: - num_elements = len(parsed_configs['subsystem_assignments']) - parsed_configs['subsystem_assignments'] = [['MISSION'] for elem in range(num_elements)] - return parsed_configs + num_elements = len(parsed_meta_data['subsystem_assignments']) + parsed_meta_data['subsystem_assignments'] = [['MISSION'] for elem in range(num_elements)] + return parsed_meta_data ##### GETTERS ################################## def get_sim_data(self): @@ -58,4 +55,20 @@ def get_just_data(self): return self.sim_data def get_vehicle_metadata(self): + print("get_vehicle_metadata") + print(self.all_headers) return self.all_headers, self.binning_configs['test_assignments'] + + # Get the data at self.index and increment the index + def get_next(self): + self.frame_index = self.frame_index + 1 + return self.sim_data[self.frame_index - 1] + + # Return whether or not the index has finished traveling through the data + def has_more(self): + return self.frame_index < len(self.sim_data) + + # Return whether or not there is data + def has_data(self): + if self.sim_data == []: + return False diff --git a/onair/data_handling/parsers/on_air_parser.py b/onair/data_handling/parsers/on_air_data_source.py similarity index 70% rename from onair/data_handling/parsers/on_air_parser.py rename to onair/data_handling/parsers/on_air_data_source.py index d61d7609..30129b0c 100644 --- a/onair/data_handling/parsers/on_air_parser.py +++ b/onair/data_handling/parsers/on_air_data_source.py @@ -10,7 +10,7 @@ from abc import ABC, abstractmethod from .parser_util import * -class OnAirParser(ABC): +class OnAirDataSource(ABC): def __init__(self, data_file, meta_file, ss_breakdown = False): """An initial parsing needs to happen in order to use the parser classes This means that, if you want to use this class to parse in real time, @@ -18,7 +18,9 @@ def __init__(self, data_file, meta_file, ss_breakdown = False): self.raw_data_file = data_file self.meta_data_file = meta_file - self.all_headers = {} + + + self.all_headers = [] self.sim_data = {} self.binning_configs = {} @@ -26,19 +28,42 @@ def __init__(self, data_file, meta_file, ss_breakdown = False): self.binning_configs['subsystem_assignments'] = configs['subsystem_assignments'] self.binning_configs['test_assignments'] = configs['test_assignments'] self.binning_configs['description_assignments'] = configs['description_assignments'] + self.all_headers = configs['data_labels'] self.process_data_file(self.raw_data_file) + @abstractmethod + def parse_meta_data_file(self, meta_data_file, ss_breakdown): + """ + Create the configs that will be used to populate the binning_configs for the data files + """ + raise NotImplementedError + @abstractmethod def process_data_file(self, data_file): """ - Adjust each individual data_file before parsing into binning_configs + TODO: Comment """ raise NotImplementedError @abstractmethod - def parse_meta_data_file(self, meta_data_file, ss_breakdown): + def get_next(self): """ - Create the configs that will be used to populate the binning_configs for the data files + Return a frame of data + """ + raise NotImplementedError + + # TODO: has_more and has_data are too confusing + @abstractmethod + def has_more(self): + """ + Used by file-based data to indicate if there are more frames (True) or if the end of the file has been reached (False) + """ + raise NotImplementedError + + @abstractmethod + def has_data(self): + """ + Used by live telemetry sources to indicate if new data has come in """ raise NotImplementedError diff --git a/onair/data_handling/parsers/tlm_json_parser.py b/onair/data_handling/parsers/tlm_json_parser.py index de236c05..2b5dbd8a 100644 --- a/onair/data_handling/parsers/tlm_json_parser.py +++ b/onair/data_handling/parsers/tlm_json_parser.py @@ -57,6 +57,7 @@ def parseTlmConfJson(file_path): configs['subsystem_assignments'] = subsys_assignments configs['test_assignments'] = mnemonic_tests configs['description_assignments'] = descriptions + configs['data_labels'] = labels return configs diff --git a/onair/src/run_scripts/execution_engine.py b/onair/src/run_scripts/execution_engine.py index 1f6d5ffc..60cb24ea 100644 --- a/onair/src/run_scripts/execution_engine.py +++ b/onair/src/run_scripts/execution_engine.py @@ -115,11 +115,8 @@ def parse_configs(self, config_filepath): def parse_data(self, parser_name, parser_file_name, data_file_name, metadata_file_name, subsystems_breakdown=False): parser = importlib.import_module('onair.data_handling.parsers.' + parser_file_name) parser_class = getattr(parser, parser_name) # This could be simplified if the parsers all extend a parser class... but this works for now - # TODO: should this us os.path.join? - tm_data_path = os.environ['RUN_PATH'] + data_file_name - tm_metadata_path = os.environ['RUN_PATH'] + metadata_file_name # TODO: This will be changed on an OnAIR Data Source - data_parser = parser_class(tm_data_path, tm_metadata_path, subsystems_breakdown) + data_parser = parser_class(data_file_name, metadata_file_name, subsystems_breakdown) self.simDataParser = data_parser def setup_sim(self): diff --git a/onair/src/run_scripts/sbn_adapter.py b/onair/src/run_scripts/sbn_adapter.py index f60a5909..7c84407a 100644 --- a/onair/src/run_scripts/sbn_adapter.py +++ b/onair/src/run_scripts/sbn_adapter.py @@ -76,7 +76,7 @@ def get_current_data(recv_msg, data_struct, app_name): class AdapterDataSource(DataSource): - # Data structure (shares code with binner.py) + # Data structure # TODO: Make init data structure better currentData = [] diff --git a/onair/src/run_scripts/sim.py b/onair/src/run_scripts/sim.py index bfef8ab2..4104148a 100644 --- a/onair/src/run_scripts/sim.py +++ b/onair/src/run_scripts/sim.py @@ -27,7 +27,6 @@ class Simulator: def __init__(self, simType, dataParser, plugin_list, SBN_Flag): self.simulator = simType - vehicle = VehicleRepresentation(*dataParser.get_vehicle_metadata()) if SBN_Flag: # TODO: This is ugly, but sbn_client is only available when built for cFS... @@ -38,8 +37,11 @@ def __init__(self, simType, dataParser, plugin_list, SBN_Flag): self.simData.connect() # this also subscribes to the msgIDs else: - # TODO: this will soon just be the dataParser - self.simData = DataSource(dataParser.get_just_data()) + #self.simData = DataSource(dataParser.get_just_data()) + self.simData = dataParser + + headers, tests = dataParser.get_vehicle_metadata() + vehicle = VehicleRepresentation(headers, tests) self.agent = Agent(vehicle, plugin_list) def run_sim(self, IO_Flag=False, dev_flag=False, viz_flag = True): @@ -50,8 +52,9 @@ def run_sim(self, IO_Flag=False, dev_flag=False, viz_flag = True): last_diagnosis = time_step last_fault = time_step + print("HEY, are we run_sim or not?") while self.simData.has_more() and time_step < MAX_STEPS: - + #print("This is the loop") next = self.simData.get_next() self.agent.reason(next) self.IO_check(time_step, IO_Flag) diff --git a/onair/src/systems/vehicle_rep.py b/onair/src/systems/vehicle_rep.py index 36973750..a072d1a4 100644 --- a/onair/src/systems/vehicle_rep.py +++ b/onair/src/systems/vehicle_rep.py @@ -18,7 +18,7 @@ from ..util.print_io import * class VehicleRepresentation: - def __init__(self, headers=[], tests=[]): # metaData is a timesynchronizer obj + def __init__(self, headers, tests): assert(len(headers) == len(tests)) self.status = Status('MISSION') self.headers = headers diff --git a/test/onair/data_handling/parsers/test_csv_parser.py b/test/onair/data_handling/parsers/test_csv_parser.py index 8c4c2bbe..d98623a5 100644 --- a/test/onair/data_handling/parsers/test_csv_parser.py +++ b/test/onair/data_handling/parsers/test_csv_parser.py @@ -31,7 +31,7 @@ def test_CSV_process_data_file_sets_instance_all_headers_item_data_file_to_retur mocker.patch.object(pytest.cut, "parse_csv_data", return_value=forced_return_parse_csv_data) - # OnAirParser initialize normally sets all headers, so unit test must set this instead + # OnAirDataSource initialize normally sets all headers, so unit test must set this instead pytest.cut.all_headers = {} # Act @@ -53,7 +53,7 @@ def test_CSV_process_data_file_sets_instance_all_headers_item_data_file_to_retur mocker.patch.object(pytest.cut, "parse_csv_data", return_value=forced_return_parse_csv_data) - # OnAirParser initialize normally sets all headers and sim_data, so unit test must set this instead + # OnAirDataSource initialize normally sets all headers and sim_data, so unit test must set this instead pytest.cut.all_headers = {} pytest.cut.sim_data = {} @@ -77,7 +77,7 @@ def test_CSV_process_data_file_sets_instance_all_headers_item_data_file_to_retur mocker.patch.object(pytest.cut, "parse_csv_data", return_value=forced_return_parse_csv_data) - # OnAirParser initialize normally sets all headers and sim_data, so unit test must set this instead + # OnAirDataSource initialize normally sets all headers and sim_data, so unit test must set this instead pytest.cut.all_headers = {} pytest.cut.sim_data = {fake_key:{}} diff --git a/test/onair/data_handling/parsers/test_on_air_parser.py b/test/onair/data_handling/parsers/test_on_air_data_source.py similarity index 71% rename from test/onair/data_handling/parsers/test_on_air_parser.py rename to test/onair/data_handling/parsers/test_on_air_data_source.py index 44bdc5f1..4f8267ce 100644 --- a/test/onair/data_handling/parsers/test_on_air_parser.py +++ b/test/onair/data_handling/parsers/test_on_air_data_source.py @@ -11,21 +11,21 @@ import pytest from mock import MagicMock -import onair.data_handling.parsers.on_air_parser as on_air_parser -from onair.data_handling.parsers.on_air_parser import OnAirParser +import onair.data_handling.parsers.on_air_data_source as on_air_data_source +from onair.data_handling.parsers.on_air_data_source import OnAirDataSource -class FakeOnAirParser(OnAirParser): +class FakeOnAirDataSource(OnAirDataSource): def process_data_file(self, data_file): super().process_data_file(data_file) def parse_meta_data_file(self, configFile, ss_breakdown): super().parse_meta_data_file(configFile, ss_breakdown) -class IncompleteOnAirParser(OnAirParser): +class IncompleteOnAirDataSource(OnAirDataSource): pass -class BadFakeOnAirParser(OnAirParser): +class BadFakeOnAirDataSource(OnAirDataSource): def process_data_file(self, data_file): return super().process_data_file(data_file) @@ -34,11 +34,11 @@ def parse_meta_data_file(self, configFile, ss_breakdown): @pytest.fixture def setup_teardown(): - pytest.cut = FakeOnAirParser.__new__(FakeOnAirParser) + pytest.cut = FakeOnAirDataSource.__new__(FakeOnAirDataSource) yield 'setup_teardown' # __init__ tests -def test_OnAirParser__init__sets_instance_variables_as_expected_and_calls_parse_meta_data_file_and_process_data_file(setup_teardown, mocker): +def test_OnAirDataSource__init__sets_instance_variables_as_expected_and_calls_parse_meta_data_file_and_process_data_file(setup_teardown, mocker): # Arrange arg_rawDataFile = MagicMock() arg_metadataFile = MagicMock() @@ -70,41 +70,41 @@ def test_OnAirParser__init__sets_instance_variables_as_expected_and_calls_parse_ assert pytest.cut.binning_configs['description_assignments'] == fake_configs['description_assignments'] # abstract methods tests -def test_OnAirParser_raises_error_because_of_unimplemented_abstract_methods(): +def test_OnAirDataSource_raises_error_because_of_unimplemented_abstract_methods(): # Arrange - None # Act with pytest.raises(TypeError) as e_info: - cut = OnAirParser.__new__(OnAirParser) + cut = OnAirDataSource.__new__(OnAirDataSource) # Assert - assert "Can't instantiate abstract class OnAirParser with" in e_info.__str__() + assert "Can't instantiate abstract class OnAirDataSource with" in e_info.__str__() assert "process_data_file" in e_info.__str__() assert "parse_meta_data_file" in e_info.__str__() # Incomplete plugin call tests -def test_OnAirParser_raises_error_when_an_inherited_class_is_instantiated_because_abstract_methods_are_not_implemented_by_that_class(): +def test_OnAirDataSource_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 = IncompleteOnAirParser.__new__(IncompleteOnAirParser) + cut = IncompleteOnAirDataSource.__new__(IncompleteOnAirDataSource) # Assert - assert "Can't instantiate abstract class IncompleteOnAirParser with" in e_info.__str__() + assert "Can't instantiate abstract class IncompleteOnAirDataSource with" in e_info.__str__() assert "process_data_file" in e_info.__str__() assert "parse_meta_data_file" in e_info.__str__() -def test_OnAirParser_raises_error_when_an_inherited_class_calls_abstract_method_process_data_file(): +def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_process_data_file(): # Act - cut = BadFakeOnAirParser.__new__(BadFakeOnAirParser) + cut = BadFakeOnAirDataSource.__new__(BadFakeOnAirDataSource) # populate list with the functions that should raise exceptions when called. with pytest.raises(NotImplementedError) as e_info: cut.process_data_file(None) assert "NotImplementedError" in e_info.__str__() -def test_OnAirParser_raises_error_when_an_inherited_class_calls_abstract_method_parse_meta_data_file(): +def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_parse_meta_data_file(): # Act - cut = BadFakeOnAirParser.__new__(BadFakeOnAirParser) + cut = BadFakeOnAirDataSource.__new__(BadFakeOnAirDataSource) # populate list with the functions that should raise exceptions when called. with pytest.raises(NotImplementedError) as e_info: From 9fcecbcb023835a9594333489da2d36d6e0513b8 Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Thu, 21 Sep 2023 13:16:14 -0400 Subject: [PATCH 2/9] Fix broken unit tests --- .../data_handling/parsers/test_csv_parser.py | 73 +++---------------- .../parsers/test_on_air_data_source.py | 21 +++++- .../parsers/test_tlm_json_parser.py | 18 ++++- .../src/run_scripts/test_execution_engine.py | 7 +- test/onair/src/run_scripts/test_sim.py | 3 +- test/onair/src/systems/test_vehicle_rep.py | 17 ----- 6 files changed, 48 insertions(+), 91 deletions(-) diff --git a/test/onair/data_handling/parsers/test_csv_parser.py b/test/onair/data_handling/parsers/test_csv_parser.py index d98623a5..bdd12afe 100644 --- a/test/onair/data_handling/parsers/test_csv_parser.py +++ b/test/onair/data_handling/parsers/test_csv_parser.py @@ -20,76 +20,23 @@ def setup_teardown(): yield 'setup_teardown' # process_data_per_data_file tests -def test_CSV_process_data_file_sets_instance_all_headers_item_data_file_to_returned_labels_item_data_file_when_returned_data_is_empty(mocker, setup_teardown): +def test_CSV_process_data_file_sets_sim_data_to_parse_csv_data_return_and_frame_index_to_zero(mocker, setup_teardown): # Arrange arg_data_file = MagicMock() - fake_label_data_file_item = MagicMock() - fake_labels = {arg_data_file:fake_label_data_file_item} - fake_data = [] - forced_return_parse_csv_data = [fake_labels, fake_data] + forced_return_parse_csv_data = MagicMock() mocker.patch.object(pytest.cut, "parse_csv_data", return_value=forced_return_parse_csv_data) - # OnAirDataSource initialize normally sets all headers, so unit test must set this instead - pytest.cut.all_headers = {} - - # Act - pytest.cut.process_data_file(arg_data_file) - - # Assert - assert pytest.cut.all_headers[arg_data_file] == fake_label_data_file_item - -def test_CSV_process_data_file_sets_instance_all_headers_item_data_file_to_returned_labels_item_data_file_and_when_data_key_is_not_in_sim_data_sets_item_key_to_dict_and_key_item_data_file_item_to_data_key_item_data_file_item(mocker, setup_teardown): - # Arrange - arg_data_file = MagicMock() - - fake_label_data_file_item = MagicMock() - fake_labels = {arg_data_file:fake_label_data_file_item} - fake_key_data_file_item = MagicMock() - fake_key = MagicMock() - fake_data = {fake_key:{arg_data_file:fake_key_data_file_item}} - forced_return_parse_csv_data = [fake_labels, fake_data] - - mocker.patch.object(pytest.cut, "parse_csv_data", return_value=forced_return_parse_csv_data) - - # OnAirDataSource initialize normally sets all headers and sim_data, so unit test must set this instead - pytest.cut.all_headers = {} - pytest.cut.sim_data = {} - # Act pytest.cut.process_data_file(arg_data_file) # Assert - assert pytest.cut.all_headers[arg_data_file] == fake_label_data_file_item - assert pytest.cut.sim_data[fake_key][arg_data_file] == fake_key_data_file_item - -def test_CSV_process_data_file_sets_instance_all_headers_item_data_file_to_returned_labels_item_data_file_and_when_data_key_is_already_in_sim_data_sets_item_key_to_dict_and_key_item_data_file_item_to_data_key_item_data_file_item(mocker, setup_teardown): - # Arrange - arg_data_file = MagicMock() - - fake_label_data_file_item = MagicMock() - fake_labels = {arg_data_file:fake_label_data_file_item} - fake_key_data_file_item = MagicMock() - fake_key = MagicMock() - fake_data = {fake_key:{arg_data_file:fake_key_data_file_item}} - forced_return_parse_csv_data = [fake_labels, fake_data] - - mocker.patch.object(pytest.cut, "parse_csv_data", return_value=forced_return_parse_csv_data) - - # OnAirDataSource initialize normally sets all headers and sim_data, so unit test must set this instead - pytest.cut.all_headers = {} - pytest.cut.sim_data = {fake_key:{}} - - # Act - pytest.cut.process_data_file(arg_data_file) - - # Assert - assert pytest.cut.all_headers[arg_data_file] == fake_label_data_file_item - assert pytest.cut.sim_data[fake_key][arg_data_file] == fake_key_data_file_item + assert pytest.cut.sim_data == forced_return_parse_csv_data + assert pytest.cut.frame_index == 0 # CSV parse_csv_data tests -def test_CSV_parse_csv_data_returns_tuple_of_empty_lists_when_parsed_dataset_from_given_dataFile_call_to_iterrows_returns_empty(mocker, setup_teardown): +def test_CSV_parse_csv_data_returns_empty_list_when_parsed_dataset_from_given_dataFile_call_to_iterrows_returns_empty(mocker, setup_teardown): # Arrange arg_dataFile = MagicMock() @@ -104,7 +51,7 @@ def test_CSV_parse_csv_data_returns_tuple_of_empty_lists_when_parsed_dataset_fro fake_second_data_set.columns = MagicMock() fake_second_data_set.columns.values = set() - expected_result = ([], []) + expected_result = [] mocker.patch(csv_parser.__name__ + '.pd.read_csv', return_value=fake_initial_data_set) mocker.patch.object(fake_columns_str, 'contains', return_value=forced_return_contains) @@ -124,7 +71,7 @@ def test_CSV_parse_csv_data_returns_tuple_of_empty_lists_when_parsed_dataset_fro assert fake_initial_data_set.loc.__getitem__.call_args_list[0].args[0][1] == ~forced_return_contains assert result == expected_result -def test_CSV_parse_csv_data_returns_tuple_of_empty_list_of_headers_and_list_of_row_values_when_parsed_dataset_from_given_dataFile_call_to_iterrows_returns_iterator(mocker, setup_teardown): +def test_CSV_parse_csv_data_returns_list_of_row_values_when_parsed_dataset_from_given_dataFile_call_to_iterrows_returns_iterator(mocker, setup_teardown): # Arrange arg_dataFile = MagicMock() @@ -150,7 +97,7 @@ def test_CSV_parse_csv_data_returns_tuple_of_empty_list_of_headers_and_list_of_r expected_result_list.append(fake_row_values) forced_return_iterrows = iter(fake_iterrows) - expected_result = ([], expected_result_list) + expected_result = expected_result_list mocker.patch(csv_parser.__name__ + '.pd.read_csv', return_value=fake_initial_data_set) mocker.patch.object(fake_columns_str, 'contains', return_value=forced_return_contains) @@ -168,7 +115,7 @@ def test_CSV_parse_csv_data_returns_tuple_of_empty_list_of_headers_and_list_of_r assert fake_columns_str.contains.call_args_list[0].args == ('^Unnamed', ) assert result == expected_result -def test_CSV_parse_csv_data_returns_tuple_of_list_of_headers_and_list_of_data_frames_call_to_iterrows_returns_iterator_and_column_names_exist(mocker, setup_teardown): +def test_CSV_parse_csv_data_returns_list_of_data_frames_call_to_iterrows_returns_iterator_and_column_names_exist(mocker, setup_teardown): # Arrange arg_dataFile = MagicMock() @@ -196,7 +143,7 @@ def test_CSV_parse_csv_data_returns_tuple_of_list_of_headers_and_list_of_data_fr expected_result_dict.append(fake_row_values) forced_return_iterrows = iter(fake_iterrows) - expected_result = (fake_second_data_set.columns.values, expected_result_dict) + expected_result = expected_result_dict mocker.patch(csv_parser.__name__ + '.pd.read_csv', return_value=fake_initial_data_set) mocker.patch.object(fake_columns_str, 'contains', return_value=forced_return_contains) diff --git a/test/onair/data_handling/parsers/test_on_air_data_source.py b/test/onair/data_handling/parsers/test_on_air_data_source.py index 4f8267ce..3301570c 100644 --- a/test/onair/data_handling/parsers/test_on_air_data_source.py +++ b/test/onair/data_handling/parsers/test_on_air_data_source.py @@ -22,6 +22,15 @@ def process_data_file(self, data_file): def parse_meta_data_file(self, configFile, ss_breakdown): super().parse_meta_data_file(configFile, ss_breakdown) + def get_next(self): + return super().get_next() + + def has_more(self): + return super().has_more() + + def has_data(self): + return super().has_data() + class IncompleteOnAirDataSource(OnAirDataSource): pass @@ -32,6 +41,15 @@ def process_data_file(self, data_file): def parse_meta_data_file(self, configFile, ss_breakdown): return super().parse_meta_data_file(configFile, ss_breakdown) + def get_next(self): + return super().get_next() + + def has_more(self): + return super().has_more() + + def has_data(self): + return super().has_data() + @pytest.fixture def setup_teardown(): pytest.cut = FakeOnAirDataSource.__new__(FakeOnAirDataSource) @@ -48,6 +66,7 @@ def test_OnAirDataSource__init__sets_instance_variables_as_expected_and_calls_pa fake_configs['subsystem_assignments'] = MagicMock() fake_configs['test_assignments'] = MagicMock() fake_configs['description_assignments'] = MagicMock() + fake_configs['data_labels'] = MagicMock() mocker.patch.object(pytest.cut, 'parse_meta_data_file', return_value=fake_configs) mocker.patch.object(pytest.cut, 'process_data_file') @@ -58,7 +77,7 @@ def test_OnAirDataSource__init__sets_instance_variables_as_expected_and_calls_pa # Assert assert pytest.cut.raw_data_file == arg_rawDataFile assert pytest.cut.meta_data_file == arg_metadataFile - assert pytest.cut.all_headers == {} + assert pytest.cut.all_headers == fake_configs['data_labels'] assert pytest.cut.sim_data == {} assert pytest.cut.parse_meta_data_file.call_count == 1 assert pytest.cut.parse_meta_data_file.call_args_list[0].args == (arg_metadataFile, arg_ss_breakdown, ) diff --git a/test/onair/data_handling/parsers/test_tlm_json_parser.py b/test/onair/data_handling/parsers/test_tlm_json_parser.py index 8149771e..7f04502f 100644 --- a/test/onair/data_handling/parsers/test_tlm_json_parser.py +++ b/test/onair/data_handling/parsers/test_tlm_json_parser.py @@ -28,6 +28,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_configs_with_empty_dicts_when_ expected_result['subsystem_assignments'] = [] expected_result['test_assignments'] = [] expected_result['description_assignments'] = [] + expected_result['data_labels'] = [] # Act result = tlm_json_parser.parseTlmConfJson(arg_file_path) @@ -61,6 +62,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo expected_result['subsystem_assignments'] = [fake_subsystem] expected_result['test_assignments'] = [[[fake_mnemonics, fake_limits]]] expected_result['description_assignments'] = [fake_description] + expected_result['data_labels'] = [fake_label] # Act result = tlm_json_parser.parseTlmConfJson(arg_file_path) @@ -89,6 +91,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo expected_result['subsystem_assignments'] = [fake_subsystem] expected_result['test_assignments'] = [[['NOOP']]] expected_result['description_assignments'] = [['No description']] + expected_result['data_labels'] = [fake_label] # Act result = tlm_json_parser.parseTlmConfJson(arg_file_path) @@ -107,11 +110,13 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo fake_data = MagicMock() fake_organized_data = {} fake_subsystems = [] + fake_labels = [] num_labels = pytest.gen.randint(2, 10) # arbitrary, from 2 to 10 for i in range(num_labels): fake_label = MagicMock() fake_subsystem = MagicMock() fake_subsystems.append(fake_subsystem) + fake_labels.append(fake_label) fake_organized_data[fake_label] = {'subsystem' : fake_subsystem} mocker.patch(tlm_json_parser.__name__ + '.parseJson', return_value=fake_data) @@ -121,6 +126,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo expected_result['subsystem_assignments'] = fake_subsystems expected_result['test_assignments'] = [[['NOOP']]] * num_labels expected_result['description_assignments'] = [['No description']] * num_labels + expected_result['data_labels'] = fake_labels # Act result = tlm_json_parser.parseTlmConfJson(arg_file_path) @@ -154,6 +160,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo expected_result['subsystem_assignments'] = [fake_subsystem] expected_result['test_assignments'] = [[[fake_mnemonics, fake_limits]]] expected_result['description_assignments'] = [fake_description] + expected_result['data_labels'] = [fake_label] # Act result = tlm_json_parser.parseTlmConfJson(arg_file_path) @@ -171,13 +178,13 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo fake_data = MagicMock() num_elems = pytest.gen.randint(2, 10) # arbitrary, from 2 to 10 - fake_label = [MagicMock() for i in range(num_elems)] + fake_labels = [MagicMock() for i in range(num_elems)] fake_subsystem = MagicMock() fake_limits = MagicMock() fake_mnemonics = MagicMock() fake_description = MagicMock() fake_organized_data = {} - for label in fake_label: + for label in fake_labels: fake_organized_data[label] = {'subsystem' : fake_subsystem, 'tests' : {fake_mnemonics : fake_limits}, 'description' : fake_description} @@ -189,6 +196,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo expected_result['subsystem_assignments'] = [fake_subsystem] * num_elems expected_result['test_assignments'] = [[[fake_mnemonics, fake_limits]]] * num_elems expected_result['description_assignments'] = [fake_description] * num_elems + expected_result['data_labels'] = fake_labels # Act result = tlm_json_parser.parseTlmConfJson(arg_file_path) @@ -232,7 +240,8 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo ordered_mnemonics = [y for x, y in sorted(zip(ordering_list, fake_mnemonics))] ordered_limits = [y for x, y in sorted(zip(ordering_list, fake_limits))] ordered_descs = [y for x, y in sorted(zip(ordering_list, fake_description))] - + ordered_labels = [y for x, y in sorted(zip(ordering_list, fake_label))] + fake_organized_data = {} for i in range(num_elems): fake_organized_data[fake_label[i]] = {'subsystem' : fake_subsystem[i], @@ -246,6 +255,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo expected_result['subsystem_assignments'] = [] expected_result['test_assignments'] = [] expected_result['description_assignments'] = [] + expected_result['data_labels'] = ordered_labels for i in range(num_elems): expected_result['subsystem_assignments'].append(ordered_subsys[i]) expected_result['test_assignments'].append([[ordered_mnemonics[i], ordered_limits[i]]]) @@ -293,6 +303,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo ordered_mnemonics = [y for x, y in sorted(zip(ordering_list, fake_mnemonics))] ordered_limits = [y for x, y in sorted(zip(ordering_list, fake_limits))] ordered_descs = [y for x, y in sorted(zip(ordering_list, fake_description))] + ordered_labels = [y for x, y in sorted(zip(ordering_list, fake_label))] fake_organized_data = {} for i in range(num_elems): @@ -307,6 +318,7 @@ def test_tlm_json_parser_parseTlmConfJson_returns_expected_configs_dict_when_reo expected_result['subsystem_assignments'] = [] expected_result['test_assignments'] = [] expected_result['description_assignments'] = [] + expected_result['data_labels'] = ordered_labels for i in range(num_elems): expected_result['subsystem_assignments'].append(ordered_subsys[i]) expected_result['test_assignments'].append([[ordered_mnemonics[i], ordered_limits[i]]]) diff --git a/test/onair/src/run_scripts/test_execution_engine.py b/test/onair/src/run_scripts/test_execution_engine.py index b0e89969..31b5320b 100644 --- a/test/onair/src/run_scripts/test_execution_engine.py +++ b/test/onair/src/run_scripts/test_execution_engine.py @@ -446,14 +446,12 @@ def test_ExecutionEngine_parse_data_sets_the_simDataParser_to_the_data_parser(mo arg_subsystems_breakdown = MagicMock() class FakeParser: - def __init__(self, data_file, metad_file, subsystems_breakdown): + def __init__(self, data_file, metadata_file, subsystems_breakdown): pass fake_parser = MagicMock() fake_parser_class = FakeParser fake_parser_class_instance = MagicMock() - fake_run_path = str(MagicMock()) - fake_environ = {'RUN_PATH':fake_run_path} fake_parsed_data = MagicMock() cut = ExecutionEngine.__new__(ExecutionEngine) @@ -462,7 +460,6 @@ def __init__(self, data_file, metad_file, subsystems_breakdown): mocker.patch(execution_engine.__name__ + '.importlib.import_module', return_value=fake_parser) mocker.patch(execution_engine.__name__ + '.getattr', return_value=fake_parser_class) - mocker.patch.dict(execution_engine.__name__ + '.os.environ', fake_environ) mocker.patch.object(fake_parser_class, '__new__', return_value=fake_parser_class_instance) # Act @@ -475,7 +472,7 @@ def __init__(self, data_file, metad_file, subsystems_breakdown): assert execution_engine.getattr.call_args_list[0].args == (fake_parser, arg_parser_name,) assert cut.simDataParser == fake_parser_class_instance assert fake_parser_class.__new__.call_count == 1 - assert fake_parser_class.__new__.call_args_list[0].args == (fake_parser_class, fake_run_path + arg_dataFile, fake_run_path + arg_metadataFile, arg_subsystems_breakdown, ) + assert fake_parser_class.__new__.call_args_list[0].args == (fake_parser_class, arg_dataFile, arg_metadataFile, arg_subsystems_breakdown, ) # subsystems_breakdown diff --git a/test/onair/src/run_scripts/test_sim.py b/test/onair/src/run_scripts/test_sim.py index f84c3e60..46f75ae7 100644 --- a/test/onair/src/run_scripts/test_sim.py +++ b/test/onair/src/run_scripts/test_sim.py @@ -92,8 +92,7 @@ def test_Simulator__init__creates_Vehicle_and_DataSource_from_parsed_data_and_Ag assert cut.simulator == arg_simType assert sim.VehicleRepresentation.call_count == 1 assert sim.VehicleRepresentation.call_args_list[0].args == (fake_vehicle_metadata[0], fake_vehicle_metadata[1], ) - assert sim.DataSource.call_count == 1 - assert sim.DataSource.call_args_list[0].args == (fake_sim_data, ) + assert cut.simData == arg_dataParser assert sim.Agent.call_count == 1 assert sim.Agent.call_args_list[0].args == (fake_vehicle, arg_plugin_list) assert cut.agent == fake_agent diff --git a/test/onair/src/systems/test_vehicle_rep.py b/test/onair/src/systems/test_vehicle_rep.py index 5eccfe6b..4105b962 100644 --- a/test/onair/src/systems/test_vehicle_rep.py +++ b/test/onair/src/systems/test_vehicle_rep.py @@ -68,23 +68,6 @@ def test_VehicleRepresentation__init__sets_status_to_Status_with_str_MISSION_and assert cut.test_suite == fake_test_suite assert cut.curr_data == ['-'] * fake_len -# NOTE: commonly each optional arg is tested, but because their sizes must be equal testing both at once -def test_VehicleRepresentation__init__default_given_headers_and_tests_are_both_empty_list(mocker): - # Arrange - cut = VehicleRepresentation.__new__(VehicleRepresentation) - - mocker.patch(vehicle_rep.__name__ + '.Status') - mocker.patch(vehicle_rep.__name__ + '.TelemetryTestSuite') - - # Act - cut.__init__() - - # Assert - assert cut.headers == [] - assert vehicle_rep.TelemetryTestSuite.call_count == 1 - assert vehicle_rep.TelemetryTestSuite.call_args_list[0].args == ([], []) - assert cut.curr_data == ['-'] * 0 - # update tests def test_VehicleRepresentation_update_does_not_set_any_curr_data_when_given_frame_is_vacant_and_executes_suite_with_given_frame_and_sets_status_with_suite_status(mocker): # Arrange From 3f294e349e370d1c61c4a0963cefe9fe95ce7fa7 Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Thu, 21 Sep 2023 13:21:21 -0400 Subject: [PATCH 3/9] Remove prints --- onair/data_handling/parsers/csv_parser.py | 2 -- onair/src/run_scripts/sim.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/onair/data_handling/parsers/csv_parser.py b/onair/data_handling/parsers/csv_parser.py index c918b847..6d5ca850 100644 --- a/onair/data_handling/parsers/csv_parser.py +++ b/onair/data_handling/parsers/csv_parser.py @@ -55,8 +55,6 @@ def get_just_data(self): return self.sim_data def get_vehicle_metadata(self): - print("get_vehicle_metadata") - print(self.all_headers) return self.all_headers, self.binning_configs['test_assignments'] # Get the data at self.index and increment the index diff --git a/onair/src/run_scripts/sim.py b/onair/src/run_scripts/sim.py index 4104148a..0d0b3ee8 100644 --- a/onair/src/run_scripts/sim.py +++ b/onair/src/run_scripts/sim.py @@ -52,9 +52,7 @@ def run_sim(self, IO_Flag=False, dev_flag=False, viz_flag = True): last_diagnosis = time_step last_fault = time_step - print("HEY, are we run_sim or not?") while self.simData.has_more() and time_step < MAX_STEPS: - #print("This is the loop") next = self.simData.get_next() self.agent.reason(next) self.IO_check(time_step, IO_Flag) From 2be243f4a11eecc57d12e06ac74c83d6977e6c5d Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Thu, 21 Sep 2023 14:22:16 -0400 Subject: [PATCH 4/9] Complete code coverage --- onair/data_handling/parsers/csv_parser.py | 2 + .../data_handling/parsers/test_csv_parser.py | 86 +++++++++++++++++++ .../parsers/test_on_air_data_source.py | 33 +++++++ 3 files changed, 121 insertions(+) diff --git a/onair/data_handling/parsers/csv_parser.py b/onair/data_handling/parsers/csv_parser.py index 6d5ca850..ec6c867c 100644 --- a/onair/data_handling/parsers/csv_parser.py +++ b/onair/data_handling/parsers/csv_parser.py @@ -67,6 +67,8 @@ def has_more(self): return self.frame_index < len(self.sim_data) # Return whether or not there is data + # TODO: This function may be removed with future clean-up def has_data(self): if self.sim_data == []: return False + return True diff --git a/test/onair/data_handling/parsers/test_csv_parser.py b/test/onair/data_handling/parsers/test_csv_parser.py index bdd12afe..9a52b78b 100644 --- a/test/onair/data_handling/parsers/test_csv_parser.py +++ b/test/onair/data_handling/parsers/test_csv_parser.py @@ -289,3 +289,89 @@ def test_CSV_get_vehicle_metadata_returns_list_of_headers_and_list_of_test_assig # Assert assert result == expected_result + +# CSV get_next test +def test_CSV_get_next_increments_index_and_returns_current_frame_of_data(setup_teardown): + # Arrange + fake_frame_index = 10 + fake_sim_data = [] + for i in range(fake_frame_index + 1): + fake_sim_data.append(MagicMock()) + + expected_result = fake_sim_data[fake_frame_index] + + pytest.cut.frame_index = fake_frame_index + pytest.cut.sim_data = fake_sim_data + + # Act + result = pytest.cut.get_next() + + # Assert + assert result == expected_result + assert pytest.cut.frame_index == fake_frame_index + 1 + +# CSV has_more test +def test_CSV_has_more_returns_true_when_index_less_than_number_of_frames(setup_teardown): + # Arrange + fake_frame_index = 10 + fake_sim_data = [] + for i in range(fake_frame_index + 1): + fake_sim_data.append(MagicMock()) + + expected_result = True + + pytest.cut.frame_index = 5 + pytest.cut.sim_data = fake_sim_data + + # Act + result = pytest.cut.has_more() + + # Assert + assert result == expected_result + +def test_CSV_has_more_returns_false_when_index_equal_than_number_of_frames(setup_teardown): + # Arrange + fake_frame_index = 10 + fake_sim_data = [] + for i in range(fake_frame_index): + fake_sim_data.append(MagicMock()) + + expected_result = False + + pytest.cut.frame_index = fake_frame_index + pytest.cut.sim_data = fake_sim_data + + # Act + result = pytest.cut.has_more() + + # Assert + assert result == expected_result + +# CSV has_data test +def test_CSV_has_data_returns_true_sim_data_is_non_empty(setup_teardown): + # Arrange + fake_sim_data = MagicMock() + + expected_result = True + + pytest.cut.sim_data = fake_sim_data + + # Act + result = pytest.cut.has_data() + + # Assert + assert result == expected_result + +def test_CSV_has_data_returns_false_sim_data_is_empty(setup_teardown): + # Arrange + fake_sim_data = [] + + expected_result = False + + pytest.cut.sim_data = fake_sim_data + + # Act + result = pytest.cut.has_data() + + # Assert + assert result == expected_result diff --git a/test/onair/data_handling/parsers/test_on_air_data_source.py b/test/onair/data_handling/parsers/test_on_air_data_source.py index 3301570c..7658e700 100644 --- a/test/onair/data_handling/parsers/test_on_air_data_source.py +++ b/test/onair/data_handling/parsers/test_on_air_data_source.py @@ -99,6 +99,9 @@ def test_OnAirDataSource_raises_error_because_of_unimplemented_abstract_methods( assert "Can't instantiate abstract class OnAirDataSource with" in e_info.__str__() assert "process_data_file" in e_info.__str__() assert "parse_meta_data_file" in e_info.__str__() + assert "get_next" in e_info.__str__() + assert "has_more" in e_info.__str__() + assert "has_data" in e_info.__str__() # Incomplete plugin call tests def test_OnAirDataSource_raises_error_when_an_inherited_class_is_instantiated_because_abstract_methods_are_not_implemented_by_that_class(): @@ -111,6 +114,9 @@ def test_OnAirDataSource_raises_error_when_an_inherited_class_is_instantiated_be assert "Can't instantiate abstract class IncompleteOnAirDataSource with" in e_info.__str__() assert "process_data_file" in e_info.__str__() assert "parse_meta_data_file" in e_info.__str__() + assert "get_next" in e_info.__str__() + assert "has_more" in e_info.__str__() + assert "has_data" in e_info.__str__() def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_process_data_file(): # Act @@ -129,3 +135,30 @@ def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_met with pytest.raises(NotImplementedError) as e_info: cut.parse_meta_data_file(None, None) assert "NotImplementedError" in e_info.__str__() + +def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_get_next(): + # Act + cut = BadFakeOnAirDataSource.__new__(BadFakeOnAirDataSource) + + # populate list with the functions that should raise exceptions when called. + with pytest.raises(NotImplementedError) as e_info: + cut.get_next() + assert "NotImplementedError" in e_info.__str__() + +def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_has_more(): + # Act + cut = BadFakeOnAirDataSource.__new__(BadFakeOnAirDataSource) + + # populate list with the functions that should raise exceptions when called. + with pytest.raises(NotImplementedError) as e_info: + cut.has_more() + assert "NotImplementedError" in e_info.__str__() + +def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_has_data(): + # Act + cut = BadFakeOnAirDataSource.__new__(BadFakeOnAirDataSource) + + # populate list with the functions that should raise exceptions when called. + with pytest.raises(NotImplementedError) as e_info: + cut.has_data() + assert "NotImplementedError" in e_info.__str__() From 642da0839344d9ffbddbc070fe6da4359f5baf85 Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Thu, 21 Sep 2023 14:48:12 -0400 Subject: [PATCH 5/9] Remove DataSource, move files up from parsers directory --- .../data_handling/{parsers => }/csv_parser.py | 2 +- onair/data_handling/data_source.py | 43 --- .../{parsers => }/on_air_data_source.py | 0 .../{parsers => }/parser_util.py | 0 onair/data_handling/parsers/__init__.py | 0 .../{parsers => }/tlm_json_parser.py | 0 onair/src/run_scripts/execution_engine.py | 2 +- onair/src/run_scripts/sbn_adapter.py | 6 +- onair/src/run_scripts/sim.py | 1 - test/onair/data_handling/parsers/__init__.py | 0 .../{parsers => }/test_csv_parser.py | 4 +- test/onair/data_handling/test_data_source.py | 264 ------------------ .../{parsers => }/test_on_air_data_source.py | 4 +- .../{parsers => }/test_parser_util.py | 2 +- .../{parsers => }/test_tlm_json_parser.py | 2 +- test/onair/src/run_scripts/test_sim.py | 1 - 16 files changed, 11 insertions(+), 320 deletions(-) rename onair/data_handling/{parsers => }/csv_parser.py (98%) delete mode 100644 onair/data_handling/data_source.py rename onair/data_handling/{parsers => }/on_air_data_source.py (100%) rename onair/data_handling/{parsers => }/parser_util.py (100%) delete mode 100644 onair/data_handling/parsers/__init__.py rename onair/data_handling/{parsers => }/tlm_json_parser.py (100%) delete mode 100644 test/onair/data_handling/parsers/__init__.py rename test/onair/data_handling/{parsers => }/test_csv_parser.py (99%) delete mode 100644 test/onair/data_handling/test_data_source.py rename test/onair/data_handling/{parsers => }/test_on_air_data_source.py (97%) rename test/onair/data_handling/{parsers => }/test_parser_util.py (99%) rename test/onair/data_handling/{parsers => }/test_tlm_json_parser.py (99%) diff --git a/onair/data_handling/parsers/csv_parser.py b/onair/data_handling/csv_parser.py similarity index 98% rename from onair/data_handling/parsers/csv_parser.py rename to onair/data_handling/csv_parser.py index ec6c867c..96a59dd8 100644 --- a/onair/data_handling/parsers/csv_parser.py +++ b/onair/data_handling/csv_parser.py @@ -15,7 +15,7 @@ import pandas as pd from .on_air_data_source import OnAirDataSource -from ...src.util.print_io import * +from ..src.util.print_io import * from .parser_util import * class CSV(OnAirDataSource): diff --git a/onair/data_handling/data_source.py b/onair/data_handling/data_source.py deleted file mode 100644 index 7ca8e265..00000000 --- a/onair/data_handling/data_source.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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" - -""" -DataSource class -Helper class to iterate through data -""" - -class DataSource: - def __init__(self, data=[]): - self.index = 0 - self.data = data - if data != []: - self.data_dimension = len(data[0]) - else: - self.data_dimension = 0 - - # Get the data at self.index and increment the index - def get_next(self): - #print("OH NO") - self.index = self.index + 1 - return self.data[self.index - 1] - - # Return whether or not the index has finished traveling through the data - def has_more(self): - return self.index < len(self.data) - - # Return whether or not there is data - def has_data(self): - if self.data == []: - return False - - empty_step = ['-']*(self.data_dimension - 1) - for timestep in self.data: - if timestep[1:] != empty_step: # Dont count the time data stamp - return True - return False diff --git a/onair/data_handling/parsers/on_air_data_source.py b/onair/data_handling/on_air_data_source.py similarity index 100% rename from onair/data_handling/parsers/on_air_data_source.py rename to onair/data_handling/on_air_data_source.py diff --git a/onair/data_handling/parsers/parser_util.py b/onair/data_handling/parser_util.py similarity index 100% rename from onair/data_handling/parsers/parser_util.py rename to onair/data_handling/parser_util.py diff --git a/onair/data_handling/parsers/__init__.py b/onair/data_handling/parsers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/onair/data_handling/parsers/tlm_json_parser.py b/onair/data_handling/tlm_json_parser.py similarity index 100% rename from onair/data_handling/parsers/tlm_json_parser.py rename to onair/data_handling/tlm_json_parser.py diff --git a/onair/src/run_scripts/execution_engine.py b/onair/src/run_scripts/execution_engine.py index 60cb24ea..437c92ad 100644 --- a/onair/src/run_scripts/execution_engine.py +++ b/onair/src/run_scripts/execution_engine.py @@ -113,7 +113,7 @@ def parse_configs(self, config_filepath): pass def parse_data(self, parser_name, parser_file_name, data_file_name, metadata_file_name, subsystems_breakdown=False): - parser = importlib.import_module('onair.data_handling.parsers.' + parser_file_name) + parser = importlib.import_module('onair.data_handling.' + parser_file_name) parser_class = getattr(parser, parser_name) # This could be simplified if the parsers all extend a parser class... but this works for now # TODO: This will be changed on an OnAIR Data Source data_parser = parser_class(data_file_name, metadata_file_name, subsystems_breakdown) diff --git a/onair/src/run_scripts/sbn_adapter.py b/onair/src/run_scripts/sbn_adapter.py index 7c84407a..96b367da 100644 --- a/onair/src/run_scripts/sbn_adapter.py +++ b/onair/src/run_scripts/sbn_adapter.py @@ -8,7 +8,7 @@ # See "NOSA GSC-19165-1 OnAIR.pdf" """ -AdapterDataSource class +SBN_Adapter class Receives messages from SBN, serves as a data source for sim.py """ @@ -18,7 +18,7 @@ import datetime import os -from ...data_handling.data_source import DataSource +from ...data_handling.on_air_data_source import OnAirDataSource from ctypes import * import sbn_client as sbn import message_headers as msg_hdr @@ -75,7 +75,7 @@ def get_current_data(recv_msg, data_struct, app_name): AdapterDataSource.new_data = True -class AdapterDataSource(DataSource): +class AdapterDataSource(OnAirDataSource): # Data structure # TODO: Make init data structure better currentData = [] diff --git a/onair/src/run_scripts/sim.py b/onair/src/run_scripts/sim.py index 0d0b3ee8..22299273 100644 --- a/onair/src/run_scripts/sim.py +++ b/onair/src/run_scripts/sim.py @@ -19,7 +19,6 @@ from ..util.file_io import * from ..util.print_io import * from ..util.sim_io import * -from ...data_handling.data_source import DataSource MAX_STEPS = 2050 DIAGNOSIS_INTERVAL = 100 diff --git a/test/onair/data_handling/parsers/__init__.py b/test/onair/data_handling/parsers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/onair/data_handling/parsers/test_csv_parser.py b/test/onair/data_handling/test_csv_parser.py similarity index 99% rename from test/onair/data_handling/parsers/test_csv_parser.py rename to test/onair/data_handling/test_csv_parser.py index 9a52b78b..db1eb54b 100644 --- a/test/onair/data_handling/parsers/test_csv_parser.py +++ b/test/onair/data_handling/test_csv_parser.py @@ -11,8 +11,8 @@ import pytest from mock import MagicMock -import onair.data_handling.parsers.csv_parser as csv_parser -from onair.data_handling.parsers.csv_parser import CSV +import onair.data_handling.csv_parser as csv_parser +from onair.data_handling.csv_parser import CSV @pytest.fixture def setup_teardown(): diff --git a/test/onair/data_handling/test_data_source.py b/test/onair/data_handling/test_data_source.py deleted file mode 100644 index ce23f1fe..00000000 --- a/test/onair/data_handling/test_data_source.py +++ /dev/null @@ -1,264 +0,0 @@ -# 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 DataSource Functionality """ -import pytest -from mock import MagicMock - -import onair.data_handling.data_source as data_source -from onair.data_handling.data_source import DataSource - -# __init__ tests -def test_DataSource__init__sets_index_to_0_and_data_to_given_data_and_data_dimension_to_len_of_first_data_in_data_when_given_data_occupied_and_first_data_has_len_more_than_0(): - # Arrange - arg_data = [] - - num_fake_first_data = pytest.gen.randint(1, 10) # arbitrary, from 1 to 10 (0 has own test) - fake_first_data = [] - for i in range(num_fake_first_data): - fake_first_data.append(MagicMock) - - arg_data.append(fake_first_data) - for i in range(pytest.gen.randint(0, 10)): # arbitrary, from 0 to 10 more data - arg_data.append(MagicMock()) - - cut = DataSource.__new__(DataSource) - - # Act - cut.__init__(arg_data) - - # Assert - assert cut.index == 0 - assert cut.data == arg_data - assert cut.data_dimension == num_fake_first_data - -def test_DataSource__init__sets_index_to_0_and_data_to_given_data_and_data_dimension_to_0_when_given_data_is_occupied_and_first_data_has_len_0(): - # Arrange - arg_data = [] - - fake_first_data = [] - - arg_data.append(fake_first_data) - for i in range(pytest.gen.randint(0, 10)): # arbitrary, from 0 to 10 more data - arg_data.append(MagicMock()) - - cut = DataSource.__new__(DataSource) - - # Act - cut.__init__(arg_data) - - # Assert - assert cut.index == 0 - assert cut.data == arg_data - assert cut.data_dimension == 0 - -def test_DataSource__init__sets_index_to_0_and_data_to_given_data_and_data_dimension_to_0_when_given_data_is_vacant(): - # Arrange - arg_data = [] - - cut = DataSource.__new__(DataSource) - - # Act - cut.__init__(arg_data) - - # Assert - assert cut.index == 0 - assert cut.data == arg_data - assert cut.data_dimension == 0 - -def test_DataSource__init__default_given_data_is_empty_list(): - # Arrange - cut = DataSource.__new__(DataSource) - - # Act - cut.__init__() - - # Assert - assert cut.index == 0 - assert cut.data == [] - assert cut.data_dimension == 0 - -# get_next tests -def test_DataSource_get_next_increments_index_and_returns_data_at_index_minus_1_when_index_minus_1_is_less_than_len_data(): - # Arrange - initial_index = pytest.gen.randint(0, 10) # arbitrary, from 0 to 10 - - cut = DataSource.__new__(DataSource) - cut.index = initial_index - cut.data = [] - - fake_num_data = cut.index + pytest.gen.randint(1, 5) # arbitrary, from 1 to 5 more data than index - - for i in range(fake_num_data): - cut.data.append(MagicMock()) - - # Act - result = cut.get_next() - - # Assert - assert cut.index == initial_index + 1 - assert result == cut.data[initial_index] - -def test_DataSource_get_next_raises_Exception_when_data_is_vacant_but_still_increments_index(): - # Arrange - initial_index = pytest.gen.randint(0, 10) # arbitrary, from 0 to 10 - - cut = DataSource.__new__(DataSource) - cut.index = initial_index - cut.data = [] - - # Act - with pytest.raises(Exception) as e_info: - cut.get_next() - - # Assert - assert cut.index == initial_index + 1 - assert e_info.match('list index out of range') - -def test_DataSource_get_next_raises_Exception_when_index_is_incremented_beyond_data_size(): - # Arrange - initial_index = pytest.gen.randint(0, 10) # arbitrary, from 0 to 10 - - cut = DataSource.__new__(DataSource) - cut.index = initial_index - cut.data = [] - - fake_num_data = initial_index - - for i in range(fake_num_data): - cut.data.append(MagicMock()) - - # Act - with pytest.raises(Exception) as e_info: - cut.get_next() - - # Assert - assert cut.index == initial_index + 1 - assert len(cut.data) == cut.index - 1 - assert e_info.match('list index out of range') - -# has_more tests -def test_DataSource_has_more_returns_True_when_index_is_less_than_data_len(mocker): - # Arrange - cut = DataSource.__new__(DataSource) - cut.index = pytest.gen.randint(0, 5) # arbitrary, from 0 to 5 - cut.data = MagicMock() - - mocker.patch(data_source.__name__ + '.len', return_value=cut.index+pytest.gen.randint(1, 100)) # arbitrary, from 1 to 100 more data than index - - # Act - result = cut.has_more() - - # Assert - assert data_source.len.call_count == 1 - assert data_source.len.call_args_list[0].args == (cut.data, ) - assert result == True - -def test_DataSource_has_more_returns_False_when_index_eq_data_len(mocker): - # Arrange - cut = DataSource.__new__(DataSource) - cut.index = pytest.gen.randint(0, 5) # arbitrary, from 0 to 5 - cut.data = MagicMock() - - mocker.patch(data_source.__name__ + '.len', return_value=cut.index) - - # Act - result = cut.has_more() - - # Assert - assert data_source.len.call_count == 1 - assert data_source.len.call_args_list[0].args == (cut.data, ) - assert result == False - -def test_DataSource_has_more_returns_False_when_index_greater_than_data_len(mocker): - # Arrange - cut = DataSource.__new__(DataSource) - cut.index = pytest.gen.randint(1, 5) # arbitrary, from 1 to 5 (min 1 to be able to subtract) - cut.data = MagicMock() - - mocker.patch(data_source.__name__ + '.len', return_value=cut.index-1) - - # Act - result = cut.has_more() - - # Assert - assert data_source.len.call_count == 1 - assert data_source.len.call_args_list[0].args == (cut.data, ) - assert result == False - -# has_data tests -def test_DataSource_has_data_returns_False_when_data_is_empty_list(): - # Arrange - cut = DataSource.__new__(DataSource) - cut.data = [] - - # Act - result = cut.has_data() - - # Assert - assert result == False - -def test_DataSource_has_data_returns_False_when_all_data_points_items_after_timestamp_are_empty_steps(): - # Arrange - cut = DataSource.__new__(DataSource) - cut.data_dimension = pytest.gen.randint(2, 10) # arbitrary, from 2 to 10 data size, 2 accounts for 1 as timestamp - cut.data = [] - for i in range(pytest.gen.randint(1, 10)): # arbitrary, from 1 to 10 data points - fake_data_pt = ['-'] * cut.data_dimension - fake_timestamp = MagicMock() - fake_data_pt[0] = fake_timestamp - cut.data.append(fake_data_pt) - - # Act - result = cut.has_data() - - # Assert - assert result == False - -def test_DataSource_has_data_returns_True_when_at_least_one_data_point_has_non_empty_step_after_timestamp(): - # Arrange - cut = DataSource.__new__(DataSource) - cut.data_dimension = pytest.gen.randint(2, 10) # arbitrary, from 2 to 10 data size, 2 accounts for 1 as timestamp - cut.data = [] - for i in range(pytest.gen.randint(1, 10)): # arbitrary, from 1 to 10 data points - fake_data_pt = ['-'] * cut.data_dimension - fake_timestamp = MagicMock() - fake_data_pt[0] = fake_timestamp - cut.data.append(fake_data_pt) - - cut.data[pytest.gen.randint(0, len(cut.data) - 1)][pytest.gen.randint(1, cut.data_dimension - 1)] = MagicMock() # [from 0th data pt up to last data pt][from first item after timestamp to last item] - - # Act - result = cut.has_data() - - # Assert - assert result == True - -def test_DataSource_has_data_returns_True_when_at_least_one_data_point_has_different_size_data_than_size_dimension(): - # Arrange - cut = DataSource.__new__(DataSource) - cut.data_dimension = pytest.gen.randint(2, 10) # arbitrary, from 2 to 10 data size, 2 accounts for 1 as timestamp - cut.data = [] - for i in range(pytest.gen.randint(1, 10)): # arbitrary, from 1 to 10 data points - fake_data_pt = ['-'] * cut.data_dimension - fake_timestamp = MagicMock() - fake_data_pt[0] = fake_timestamp - cut.data.append(fake_data_pt) - - if pytest.gen.randint(0, 1): - random_non_dimension_size = pytest.gen.randint(0, cut.data_dimension - 2) # less than - else: - random_non_dimension_size = pytest.gen.randint(cut.data_dimension, cut.data_dimension + 10) # greater than - cut.data[pytest.gen.randint(0, len(cut.data) - 1)] = [MagicMock()] + ['-'] * random_non_dimension_size # [from 0th data pt up to last data pt], replace random data pt with greater or less than expected dimension - - # Act - result = cut.has_data() - - # Assert - assert result == True diff --git a/test/onair/data_handling/parsers/test_on_air_data_source.py b/test/onair/data_handling/test_on_air_data_source.py similarity index 97% rename from test/onair/data_handling/parsers/test_on_air_data_source.py rename to test/onair/data_handling/test_on_air_data_source.py index 7658e700..cfe0fad1 100644 --- a/test/onair/data_handling/parsers/test_on_air_data_source.py +++ b/test/onair/data_handling/test_on_air_data_source.py @@ -11,8 +11,8 @@ import pytest from mock import MagicMock -import onair.data_handling.parsers.on_air_data_source as on_air_data_source -from onair.data_handling.parsers.on_air_data_source import OnAirDataSource +import onair.data_handling.on_air_data_source as on_air_data_source +from onair.data_handling.on_air_data_source import OnAirDataSource class FakeOnAirDataSource(OnAirDataSource): diff --git a/test/onair/data_handling/parsers/test_parser_util.py b/test/onair/data_handling/test_parser_util.py similarity index 99% rename from test/onair/data_handling/parsers/test_parser_util.py rename to test/onair/data_handling/test_parser_util.py index 5c0e485a..f9ad46a7 100644 --- a/test/onair/data_handling/parsers/test_parser_util.py +++ b/test/onair/data_handling/test_parser_util.py @@ -11,7 +11,7 @@ import pytest from mock import MagicMock -import onair.data_handling.parsers.parser_util as parser_util +import onair.data_handling.parser_util as parser_util # extract_meta_data tests def test_parser_util_extract_meta_data_raises_error_when_given_blank_meta_data_file(): diff --git a/test/onair/data_handling/parsers/test_tlm_json_parser.py b/test/onair/data_handling/test_tlm_json_parser.py similarity index 99% rename from test/onair/data_handling/parsers/test_tlm_json_parser.py rename to test/onair/data_handling/test_tlm_json_parser.py index 7f04502f..c074a023 100644 --- a/test/onair/data_handling/parsers/test_tlm_json_parser.py +++ b/test/onair/data_handling/test_tlm_json_parser.py @@ -11,7 +11,7 @@ import pytest from mock import MagicMock -import onair.data_handling.parsers.tlm_json_parser as tlm_json_parser +import onair.data_handling.tlm_json_parser as tlm_json_parser # parseTlmConfJson tests def test_tlm_json_parser_parseTlmConfJson_returns_configs_with_empty_dicts_when_reorg_dict_is_empty(mocker): diff --git a/test/onair/src/run_scripts/test_sim.py b/test/onair/src/run_scripts/test_sim.py index 46f75ae7..58e791fb 100644 --- a/test/onair/src/run_scripts/test_sim.py +++ b/test/onair/src/run_scripts/test_sim.py @@ -82,7 +82,6 @@ def test_Simulator__init__creates_Vehicle_and_DataSource_from_parsed_data_and_Ag mocker.patch.object(arg_dataParser, 'get_vehicle_metadata', return_value=fake_vehicle_metadata) mocker.patch(sim.__name__ + '.VehicleRepresentation', return_value=fake_vehicle) mocker.patch.object(arg_dataParser, 'get_just_data', return_value=fake_sim_data) - mocker.patch(sim.__name__ + '.DataSource', return_value=fake_simData) mocker.patch(sim.__name__ + '.Agent', return_value=fake_agent) # Act From 65e0a88c3a1ab99d3602172fc257432e8a8265d0 Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Thu, 21 Sep 2023 16:10:37 -0400 Subject: [PATCH 6/9] Turns out has_data isn't used by anyone --- onair/data_handling/csv_parser.py | 7 ----- onair/data_handling/on_air_data_source.py | 7 ----- test/onair/data_handling/test_csv_parser.py | 29 ------------------- .../data_handling/test_on_air_data_source.py | 17 ----------- 4 files changed, 60 deletions(-) diff --git a/onair/data_handling/csv_parser.py b/onair/data_handling/csv_parser.py index 96a59dd8..a5bb00b6 100644 --- a/onair/data_handling/csv_parser.py +++ b/onair/data_handling/csv_parser.py @@ -65,10 +65,3 @@ def get_next(self): # Return whether or not the index has finished traveling through the data def has_more(self): return self.frame_index < len(self.sim_data) - - # Return whether or not there is data - # TODO: This function may be removed with future clean-up - def has_data(self): - if self.sim_data == []: - return False - return True diff --git a/onair/data_handling/on_air_data_source.py b/onair/data_handling/on_air_data_source.py index 30129b0c..ce9a509b 100644 --- a/onair/data_handling/on_air_data_source.py +++ b/onair/data_handling/on_air_data_source.py @@ -60,10 +60,3 @@ def has_more(self): Used by file-based data to indicate if there are more frames (True) or if the end of the file has been reached (False) """ raise NotImplementedError - - @abstractmethod - def has_data(self): - """ - Used by live telemetry sources to indicate if new data has come in - """ - raise NotImplementedError diff --git a/test/onair/data_handling/test_csv_parser.py b/test/onair/data_handling/test_csv_parser.py index db1eb54b..fb1891fc 100644 --- a/test/onair/data_handling/test_csv_parser.py +++ b/test/onair/data_handling/test_csv_parser.py @@ -346,32 +346,3 @@ def test_CSV_has_more_returns_false_when_index_equal_than_number_of_frames(setup # Assert assert result == expected_result - -# CSV has_data test -def test_CSV_has_data_returns_true_sim_data_is_non_empty(setup_teardown): - # Arrange - fake_sim_data = MagicMock() - - expected_result = True - - pytest.cut.sim_data = fake_sim_data - - # Act - result = pytest.cut.has_data() - - # Assert - assert result == expected_result - -def test_CSV_has_data_returns_false_sim_data_is_empty(setup_teardown): - # Arrange - fake_sim_data = [] - - expected_result = False - - pytest.cut.sim_data = fake_sim_data - - # Act - result = pytest.cut.has_data() - - # Assert - assert result == expected_result diff --git a/test/onair/data_handling/test_on_air_data_source.py b/test/onair/data_handling/test_on_air_data_source.py index cfe0fad1..d5d8b1c6 100644 --- a/test/onair/data_handling/test_on_air_data_source.py +++ b/test/onair/data_handling/test_on_air_data_source.py @@ -28,9 +28,6 @@ def get_next(self): def has_more(self): return super().has_more() - def has_data(self): - return super().has_data() - class IncompleteOnAirDataSource(OnAirDataSource): pass @@ -47,9 +44,6 @@ def get_next(self): def has_more(self): return super().has_more() - def has_data(self): - return super().has_data() - @pytest.fixture def setup_teardown(): pytest.cut = FakeOnAirDataSource.__new__(FakeOnAirDataSource) @@ -101,7 +95,6 @@ def test_OnAirDataSource_raises_error_because_of_unimplemented_abstract_methods( assert "parse_meta_data_file" in e_info.__str__() assert "get_next" in e_info.__str__() assert "has_more" in e_info.__str__() - assert "has_data" in e_info.__str__() # Incomplete plugin call tests def test_OnAirDataSource_raises_error_when_an_inherited_class_is_instantiated_because_abstract_methods_are_not_implemented_by_that_class(): @@ -116,7 +109,6 @@ def test_OnAirDataSource_raises_error_when_an_inherited_class_is_instantiated_be assert "parse_meta_data_file" in e_info.__str__() assert "get_next" in e_info.__str__() assert "has_more" in e_info.__str__() - assert "has_data" in e_info.__str__() def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_process_data_file(): # Act @@ -153,12 +145,3 @@ def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_met with pytest.raises(NotImplementedError) as e_info: cut.has_more() assert "NotImplementedError" in e_info.__str__() - -def test_OnAirDataSource_raises_error_when_an_inherited_class_calls_abstract_method_has_data(): - # Act - cut = BadFakeOnAirDataSource.__new__(BadFakeOnAirDataSource) - - # populate list with the functions that should raise exceptions when called. - with pytest.raises(NotImplementedError) as e_info: - cut.has_data() - assert "NotImplementedError" in e_info.__str__() From e10475010acd8339abd8f906b30ee87f08d29054 Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Fri, 22 Sep 2023 13:55:12 -0400 Subject: [PATCH 7/9] Remove sbn adapter (track #42 to reintegrate) --- onair/config/adapter_config.ini | 16 - onair/src/run_scripts/sbn_adapter.py | 151 ----- .../onair/src/run_scripts/test_sbn_adapter.py | 635 ------------------ 3 files changed, 802 deletions(-) delete mode 100644 onair/config/adapter_config.ini delete mode 100644 onair/src/run_scripts/sbn_adapter.py delete mode 100644 test/onair/src/run_scripts/test_sbn_adapter.py diff --git a/onair/config/adapter_config.ini b/onair/config/adapter_config.ini deleted file mode 100644 index 7061dab6..00000000 --- a/onair/config/adapter_config.ini +++ /dev/null @@ -1,16 +0,0 @@ -# This config file sets up OnAIR to pull in data from the cFS Software Bus - -[DEFAULT] -TelemetryDataFilePath = data/raw_telemetry_data/ -TelemetryFile = adapter_TLM.txt -TelemetryMetadataFilePath = data/telemetry_configs/ -MetaFile = adapter_TLM_CONFIG.json -ParserFileName = forty_two_parser -ParserName = FortyTwo -SimName = Adapter - -[RUN_FLAGS] -IO_Flag = true -Dev_Flag = false -SBN_Flag = true -Viz_Flag = false diff --git a/onair/src/run_scripts/sbn_adapter.py b/onair/src/run_scripts/sbn_adapter.py deleted file mode 100644 index 96b367da..00000000 --- a/onair/src/run_scripts/sbn_adapter.py +++ /dev/null @@ -1,151 +0,0 @@ -# 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" - -""" -SBN_Adapter class - -Receives messages from SBN, serves as a data source for sim.py -""" - -import threading -import time -import datetime -import os - -from ...data_handling.on_air_data_source import OnAirDataSource -from ctypes import * -import sbn_client as sbn -import message_headers as msg_hdr - -# Note: The double buffer does not clear between switching. If fresh data doesn't come in, stale data is returned (delayed by 1 frame) - -# msgID_lookup_table format { msgID : [ "" , msg_hdr. , "" ] } -msgID_lookup_table = {0x0885 : ["SAMPLE", msg_hdr.sample_data_tlm_t], - 0x0887 : ["SAMPLE", msg_hdr.sample_data_power_t], - 0x0889 : ["SAMPLE", msg_hdr.sample_data_thermal_t], - 0x088A : ["SAMPLE", msg_hdr.sample_data_gps_t]} - -def message_listener_thread(): - """Thread to listen for incoming messages from SBN""" - - while(True): - generic_recv_msg_p = POINTER(sbn.sbn_data_generic_t)() - sbn.recv_msg(generic_recv_msg_p) - - msgID = generic_recv_msg_p.contents.TlmHeader.Primary.StreamId - app_name, data_struct = msgID_lookup_table[msgID] - - recv_msg_p = POINTER(data_struct)() - recv_msg_p.contents = generic_recv_msg_p.contents - recv_msg = recv_msg_p.contents - - # prints out the data from the message to the terminal - print(", ".join([field_name + ": " + str(getattr(recv_msg, field_name)) for field_name, field_type in recv_msg._fields_[1:]])) - - # TODO: Lock needed here? - get_current_data(recv_msg, data_struct, app_name) - -def get_current_data(recv_msg, data_struct, app_name): - # TODO: Lock needed here? - current_buffer = AdapterDataSource.currentData[(AdapterDataSource.double_buffer_read_index + 1) %2] - secondary_header = recv_msg.TlmHeader.Secondary - - #gets seconds from header and adds to current buffer - start_time = datetime.datetime(1969, 12, 31, 20) # January 1, 1980 - seconds = secondary_header.Seconds - subseconds = secondary_header.Subseconds - curr_time = seconds + (2**(-32) * subseconds) # a subsecond is equal to 2^-32 second - time = start_time + datetime.timedelta(seconds=curr_time) - str_time = time.strftime("%Y-%j-%H:%M:%S.%f") - current_buffer['data'][0] = str_time - - for field_name, field_type in data_struct._fields_[1:]: - header_name = app_name + "." + data_struct.__name__ + "." + str(field_name) - idx = current_buffer['headers'].index(header_name) - data = str(getattr(recv_msg, field_name)) - current_buffer['data'][idx] = data - - with AdapterDataSource.new_data_lock: - AdapterDataSource.new_data = True - - -class AdapterDataSource(OnAirDataSource): - # Data structure - # TODO: Make init data structure better - currentData = [] - - for x in range(0,2): - currentData.append({'headers' : [], 'data' : []}) - #print("Index {}".format(x)) - - # First element is always the time, set to a dummy value here - currentData[x]['headers'].append('TIME') - currentData[x]['data'].append('2000-001-12:00:00.000000000') - - for msgID in msgID_lookup_table.keys(): - app_name, data_struct = msgID_lookup_table[msgID] - struct_name = data_struct.__name__ - for field_name, field_type in data_struct._fields_[1:]: - currentData[x]['headers'].append(app_name + "." + struct_name + "." + str(field_name)) - currentData[x]['data'].extend([0]*len(data_struct._fields_[1:])) #initialize all the data arrays with zero - - new_data_lock = threading.Lock() - new_data = False - double_buffer_read_index = 0 - - def connect(self): - """Establish connection to SBN and launch listener thread.""" - time.sleep(2) - os.chdir("cf") - sbn.sbn_load_and_init() - print("SBN_Adapter Running") - - # Launch thread to listen for messages - self.listener_thread = threading.Thread(target=message_listener_thread) - self.listener_thread.start() - - # subscribe to message IDs - for msgID in msgID_lookup_table.keys(): - sbn.subscribe(msgID) - - def subscribe_message(self, msgid): - """Specify cFS message id to listen for. Unstable""" - - if isinstance(msgid, list): - for mID in msgid: - sbn.subscribe(mID) - else: - sbn.subscribe(msgid) - - def get_next(self): - """Provides the latest data from SBN in a dictionary of lists structure. - Returned data is safe to use until the next get_next call. - Blocks until new data is available.""" - - data_available = False - - while not data_available: - with AdapterDataSource.new_data_lock: - data_available = AdapterDataSource.new_data - - if not data_available: - time.sleep(0.01) - - read_index = 0 - with AdapterDataSource.new_data_lock: - AdapterDataSource.new_data = False - AdapterDataSource.double_buffer_read_index = (AdapterDataSource.double_buffer_read_index + 1) % 2 - read_index = AdapterDataSource.double_buffer_read_index - - print("Reading buffer: {}".format(read_index)) - return self.currentData[read_index]['data'] - - def has_more(self): - """Returns true if the adapter has more data. Always true: connection should be live as long as cFS is running""" - return True diff --git a/test/onair/src/run_scripts/test_sbn_adapter.py b/test/onair/src/run_scripts/test_sbn_adapter.py deleted file mode 100644 index 1dcadf11..00000000 --- a/test/onair/src/run_scripts/test_sbn_adapter.py +++ /dev/null @@ -1,635 +0,0 @@ -# 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" - -import pytest -from mock import MagicMock, PropertyMock - -import onair.src.run_scripts.sbn_adapter as sbn_adapter - -from importlib import reload -import sys - -@pytest.fixture -def setup_teardown(): - # # refresh sbn_adapter module before each test to ensure independence - reload(sbn_adapter) - - print('setup') - - yield 'setup_teardown' - - print('teardown') - - # refresh message_headers module after each test to remove any changes from testing - del sys.modules['message_headers'] - mh = MagicMock() - mh.sample_data_tlm_t = MagicMock() - mh.sample_data_tlm_t.__name__ = 'mock_sample_data_tlm_t' - mh.sample_data_power_t = MagicMock() - mh.sample_data_power_t.__name__ = 'mock_sample_data_power_t' - mh.sample_data_thermal_t = MagicMock() - mh.sample_data_thermal_t.__name__ = 'mock_sample_data_thermal_t' - mh.sample_data_gps_t = MagicMock() - mh.sample_data_gps_t.__name__ = 'mock_sample_data_gps_t' - sys.modules['message_headers'] = mh - - -class FakeSbnDataGenericT(MagicMock): - pass - -class FakeDataStruct(MagicMock): - pass - -# testing that the msgID_lookup_table is as we expect allows us to safely reference it in other tests -def test_sbn_adapter_msgID_lookup_table_is_expected_value(): - # Arrange - expected_msgID_lookup_table = {0x0885 : ["SAMPLE", sbn_adapter.msg_hdr.sample_data_tlm_t], - 0x0887 : ["SAMPLE", sbn_adapter.msg_hdr.sample_data_power_t], - 0x0889 : ["SAMPLE", sbn_adapter.msg_hdr.sample_data_thermal_t], - 0x088A : ["SAMPLE", sbn_adapter.msg_hdr.sample_data_gps_t]} - - # Act - # Assert - assert sbn_adapter.msgID_lookup_table == expected_msgID_lookup_table - -def test_sbn_adapter_message_listener_thread_loops_indefinitely_until_purposely_broken(mocker, setup_teardown): - # Arrange - fake_generic_recv_msg_p = MagicMock() - fake_recv_msg_p = MagicMock() - - fake_sbn = MagicMock() - fake_sbn.sbn_data_generic_t = PropertyMock() - - fake_generic_recv_msg_p_contents = PropertyMock() - fake_generic_recv_msg_p_TlmHeader = PropertyMock() - fake_generic_recv_msg_p_Primary = PropertyMock() - - fake_generic_recv_msg_p.contents = fake_generic_recv_msg_p_contents - fake_generic_recv_msg_p.contents.TlmHeader = fake_generic_recv_msg_p_TlmHeader - fake_generic_recv_msg_p.contents.TlmHeader.Primary = fake_generic_recv_msg_p_Primary - fake_msgID = pytest.gen.choice(list(sbn_adapter.msgID_lookup_table.keys())) - fake_generic_recv_msg_p.contents.TlmHeader.Primary.StreamId = fake_msgID - - # this exception will be used to forcefully exit the message_listener_thread function's while(True) loop - exception_message = 'forced loop exit' - exit_exception = Exception(exception_message) - - # sets return value of POINTER function to return fake_pointer an arbitrary number of times, then return exit_exception - num_loop_iterations = pytest.gen.randint(1, 10) # arbitrary, 1 to 10 - side_effect_list = ([''] * num_loop_iterations) # one item for each loop - side_effect_list.append(exit_exception) # short-circuit exit while(True) loop - - fake__fields_ = [["1st item placeholder"]] - num_fake_prints = pytest.gen.randint(1, 10) # arbitrary from 1 to 10 - fake_field_names = [] - fake_attr_values = [] - expected_print_string = '' - for i in range(num_fake_prints): - fake_attr_name = str(MagicMock()) - - print(f"fake_attr_name ", i, " ", fake_attr_name) - fake_attr_value = MagicMock() - - print(f"fake_attr_value ", i, " ", fake_attr_value) - fake_field_names.append(fake_attr_name) - fake_attr_values.append(fake_attr_value) - fake__fields_.append([fake_attr_name, fake_attr_value]) - expected_print_string += fake_attr_name + ": " + str(fake_attr_value) + ", " - - fake_generic_recv_msg_p_contents._fields_ = fake__fields_ - expected_print_string = expected_print_string[0:-2] - - mocker.patch(sbn_adapter.__name__ + '.sbn', fake_sbn) - pointer_side_effects = [FakeSbnDataGenericT, FakeDataStruct] * (num_loop_iterations + 1) - mocker.patch(sbn_adapter.__name__ + '.POINTER', side_effect=pointer_side_effects) - mocker.patch.object(FakeSbnDataGenericT, '__new__', return_value=fake_generic_recv_msg_p) - mocker.patch.object(fake_sbn, 'recv_msg', return_value=None) - mocker.patch.object(FakeDataStruct, '__new__', return_value=fake_recv_msg_p) - mocker.patch(sbn_adapter.__name__ + '.getattr', side_effect=fake_attr_values * (num_loop_iterations + 1)) - mocker.patch(sbn_adapter.__name__ + '.print', return_value=None) - - mocker.patch(sbn_adapter.__name__ + '.get_current_data', side_effect=side_effect_list) - - # Act - with pytest.raises(Exception) as e_info: - sbn_adapter.message_listener_thread() - - # Assert - assert e_info.match(exception_message) - assert sbn_adapter.POINTER.call_count == (num_loop_iterations + 1) * 2 - for i in range(num_loop_iterations + 1): - assert sbn_adapter.POINTER.call_args_list[i*2].args == (fake_sbn.sbn_data_generic_t, ) - assert sbn_adapter.POINTER.call_args_list[(i*2)+1].args == (sbn_adapter.msgID_lookup_table[fake_msgID][1], ) - assert FakeSbnDataGenericT.__new__.call_count == num_loop_iterations + 1 - assert FakeDataStruct.__new__.call_count == num_loop_iterations + 1 - assert fake_sbn.recv_msg.call_count == num_loop_iterations + 1 - for i in range(num_loop_iterations + 1): - assert fake_sbn.recv_msg.call_args_list[i].args == (fake_generic_recv_msg_p, ) - assert sbn_adapter.getattr.call_count == (num_loop_iterations + 1) * num_fake_prints - for i in range((num_loop_iterations + 1) * num_fake_prints): - assert sbn_adapter.getattr.call_args_list[i].args == (fake_generic_recv_msg_p_contents, fake_field_names[i % len(fake_field_names)]) - assert sbn_adapter.print.call_count == num_loop_iterations + 1 - for i in range(num_loop_iterations + 1): - assert sbn_adapter.print.call_args_list[i].args == (expected_print_string, ) - assert sbn_adapter.get_current_data.call_count == num_loop_iterations + 1 - for i in range(num_loop_iterations + 1): - assert sbn_adapter.get_current_data.call_args_list[i].args == (fake_generic_recv_msg_p_contents, sbn_adapter.msgID_lookup_table[fake_msgID][1], sbn_adapter.msgID_lookup_table[fake_msgID][0]) - -def test_get_current_data_with_no_fields_in_recv_msg_or_data_struct(mocker, setup_teardown): - # Arrange - fake_generic_recv_msg_p = MagicMock() - - fake_AdapterDataSource = MagicMock - fake_AdapterDataSource.currentData = {} - fake_AdapterDataSource.double_buffer_read_index = 1 - fake_AdapterDataSource.new_data_lock = PropertyMock() - fake_current_buffer = {} - fake_current_buffer['data'] = ['placeholder'] - - fake_AdapterDataSource.currentData[0] = fake_current_buffer - - arg_recv_msg = PropertyMock() - fake_generic_recv_msg_p_TlmHeader = PropertyMock() - fake_generic_recv_msg_p_Primary = PropertyMock() - fake_recv_msg_p_Secondary = PropertyMock() - - fake_generic_recv_msg_p.contents = arg_recv_msg - fake_generic_recv_msg_p.contents.TlmHeader = fake_generic_recv_msg_p_TlmHeader - fake_generic_recv_msg_p.contents.TlmHeader.Primary = fake_generic_recv_msg_p_Primary - fake_msgID = pytest.gen.choice(list(sbn_adapter.msgID_lookup_table.keys())) - fake_generic_recv_msg_p.contents.TlmHeader.Primary.StreamId = fake_msgID - arg_recv_msg.TlmHeader.Secondary = fake_recv_msg_p_Secondary - fake_seconds = pytest.gen.randint(0,59) - fake_recv_msg_p_Secondary.Seconds = fake_seconds - fake_subseconds = pytest.gen.randint(0,999) - fake_recv_msg_p_Secondary.Subseconds = fake_subseconds - fake_start_time = MagicMock() - fake_timedelta = MagicMock() - fake_time = MagicMock() - fake_str_time = MagicMock() - - mocker.patch(sbn_adapter.__name__ + '.AdapterDataSource',fake_AdapterDataSource) - mocker.patch(sbn_adapter.__name__ + '.datetime.datetime', return_value=fake_start_time) - mocker.patch(sbn_adapter.__name__ + '.datetime.timedelta', return_value=fake_timedelta) - mocker.patch.object(fake_start_time, '__add__', return_value=fake_time) - mocker.patch.object(fake_time, 'strftime', return_value=fake_str_time) - fake_AdapterDataSource.new_data = False # not required, but helps verify it gets changed to True - mocker.patch.object(fake_AdapterDataSource.new_data_lock, '__enter__') # __enter__ is used by keyword 'with' in python - - arg_data_struct = sbn_adapter.msgID_lookup_table[fake_msgID][1] - arg_app_name = sbn_adapter.msgID_lookup_table[fake_msgID][0] - - # Act - sbn_adapter.get_current_data(arg_recv_msg, arg_data_struct, arg_app_name) - - # Assert - assert sbn_adapter.datetime.datetime.call_count == 1 - assert sbn_adapter.datetime.datetime.call_args_list[0].args == (1969, 12, 31, 20) - assert sbn_adapter.datetime.timedelta.call_count == 1 - assert sbn_adapter.datetime.timedelta.call_args_list[0].kwargs == {'seconds':fake_seconds + (2**(-32) * fake_subseconds)} - # Although patched and does return value, fake_start_time.__add__ does not count but leaving comments here to show we would do this if able - # assert fake_start_time.__add__.call_count == num_loop_iterations + 1 - # for i in range(num_loop_iterations + 1): - # assert fake_start_time.__add__.call_args_list[i].args == (fake_timedelta, ) - assert fake_time.strftime.call_count == 1 - assert fake_time.strftime.call_args_list[0].args == ("%Y-%j-%H:%M:%S.%f", ) - assert fake_AdapterDataSource.new_data == True - assert fake_current_buffer['data'][0] == fake_str_time - -def test_get_current_data_with_fields_in_recv_msg_and_data_struct(mocker, setup_teardown): - # Arrange - fake_generic_recv_msg_p = MagicMock() - - fake_AdapterDataSource = MagicMock() - fake_AdapterDataSource.currentData = {} - fake_AdapterDataSource.double_buffer_read_index = 1 - fake_AdapterDataSource.new_data_lock = PropertyMock() - fake_AdapterDataSource.new_data_lock.__enter__ = MagicMock() - fake_AdapterDataSource.new_data_lock.__exit__ = MagicMock() - fake_current_buffer = {} - fake_current_buffer['data'] = ['placeholder0', 'placeholder1'] - fake_headers_for_current_buffer = MagicMock() - fake_current_buffer['headers'] = fake_headers_for_current_buffer - fake_idx = 1 - fake_AdapterDataSource.currentData[0] = fake_current_buffer - arg_recv_msg = PropertyMock() - fake_generic_recv_msg_p_TlmHeader = PropertyMock() - fake_generic_recv_msg_p_Primary = PropertyMock() - fake_recv_msg_p_Secondary = PropertyMock() - - fake_generic_recv_msg_p.contents = arg_recv_msg - fake_generic_recv_msg_p.contents.TlmHeader = fake_generic_recv_msg_p_TlmHeader - fake_generic_recv_msg_p.contents.TlmHeader.Primary = fake_generic_recv_msg_p_Primary - fake_msgID = pytest.gen.choice(list(sbn_adapter.msgID_lookup_table.keys())) - fake_generic_recv_msg_p.contents.TlmHeader.Primary.StreamId = fake_msgID - arg_recv_msg.TlmHeader.Secondary = fake_recv_msg_p_Secondary - fake_seconds = pytest.gen.randint(0,59) - fake_recv_msg_p_Secondary.Seconds = fake_seconds - fake_subseconds = pytest.gen.randint(0,999) - fake_recv_msg_p_Secondary.Subseconds = fake_subseconds - fake_start_time = MagicMock() - fake_timedelta = MagicMock() - fake_time = MagicMock() - fake_str_time = MagicMock() - - fake__fields_ = [["1st item placeholder"]] - num_fake__fields_ = pytest.gen.randint(1, 10) # arbitrary from 1 to 10 - fake_field_names = [] - for i in range(num_fake__fields_): - fake_attr_name = str(MagicMock()) - fake_attr_value = MagicMock() - - fake_field_names.append(fake_attr_name) - arg_recv_msg.__setattr__(fake_attr_name, fake_attr_value) - sbn_adapter.msgID_lookup_table[fake_msgID][1].__setattr__(fake_attr_name, fake_attr_value) - fake__fields_.append([fake_attr_name, fake_attr_value]) - - arg_recv_msg._fields_ = fake__fields_ - sbn_adapter.msgID_lookup_table[fake_msgID][1]._fields_ = fake__fields_ - - mocker.patch(sbn_adapter.__name__ + '.AdapterDataSource',fake_AdapterDataSource) - mocker.patch(sbn_adapter.__name__ + '.datetime.datetime', return_value=fake_start_time) - mocker.patch(sbn_adapter.__name__ + '.datetime.timedelta', return_value=fake_timedelta) - mocker.patch.object(fake_start_time, '__add__', return_value=fake_time) - mocker.patch.object(fake_time, 'strftime', return_value=fake_str_time) - fake_AdapterDataSource.new_data = False # not required, but helps verify it gets changed to True - mocker.patch.object(fake_AdapterDataSource.new_data_lock, '__enter__') # __enter__ is used by keyword 'with' in python - mocker.patch.object(fake_headers_for_current_buffer, 'index', return_value=fake_idx) - - arg_data_struct = sbn_adapter.msgID_lookup_table[fake_msgID][1] - arg_app_name = sbn_adapter.msgID_lookup_table[fake_msgID][0] - - # Act - sbn_adapter.get_current_data(arg_recv_msg, arg_data_struct, arg_app_name) - - # Assert - assert sbn_adapter.datetime.datetime.call_count == 1 - assert sbn_adapter.datetime.datetime.call_args_list[0].args == (1969, 12, 31, 20) - assert sbn_adapter.datetime.timedelta.call_count == 1 - assert sbn_adapter.datetime.timedelta.call_args_list[0].kwargs == {'seconds':fake_seconds + (2**(-32) * fake_subseconds)} - # Although patched and does return value, fake_start_time.__add__ does not count but leaving comments here to show we would do this if able - # assert fake_start_time.__add__.call_count == num_loop_iterations + 1 - # for i in range(num_loop_iterations + 1): - # assert fake_start_time.__add__.call_args_list[i].args == (fake_timedelta, ) - assert fake_time.strftime.call_count == 1 - assert fake_time.strftime.call_args_list[0].args == ("%Y-%j-%H:%M:%S.%f", ) - assert fake_headers_for_current_buffer.index.call_count == num_fake__fields_ - for i in range(num_fake__fields_): - assert fake_headers_for_current_buffer.index.call_args_list[i].args == (str((sbn_adapter.msgID_lookup_table[fake_msgID][0]) + "." + str(sbn_adapter.msgID_lookup_table[fake_msgID][1].__name__) + "." + fake_field_names[i]), ) - assert fake_AdapterDataSource.new_data == True - assert fake_current_buffer['data'][0] == fake_str_time - -# ---------- Tests for AdapterDataSource class --------- - -# tests for AdapterDataSource variables -def test_sbn_adapter_AdapterDataSource_current_data_equals_expected_value_when_no__fields__exist_in_data_struct(setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - lookup_table = sbn_adapter.msgID_lookup_table - - expected_current_data = [] - - for x in range(2): - expected_current_data.append({'headers' : [], 'data' : []}) - expected_current_data[x]['headers'].append('TIME') - expected_current_data[x]['data'].append('2000-001-12:00:00.000000000') - - for msgID in lookup_table.keys(): - app_name, data_struct = lookup_table[msgID] - struct_name = data_struct.__name__ - expected_current_data[x]['data'].extend([0]*len(data_struct._fields_[1:])) #initialize all the data arrays with zero - - # Act - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Assert - assert cut.currentData == expected_current_data - -def test_sbn_adapter_AdapterDataSource_current_data_equals_expected_value_when__fields___do_exist_in_data_struct(setup_teardown): - # Arrange - lookup_table = sbn_adapter.msgID_lookup_table - - expected_current_data = [] - - for x in range(2): - expected_current_data.append({'headers' : [], 'data' : []}) - expected_current_data[x]['headers'].append('TIME') - expected_current_data[x]['data'].append('2000-001-12:00:00.000000000') - for msgID in lookup_table.keys(): - app_name, data_struct = lookup_table[msgID] - struct_name = data_struct.__name__ - - fake__fields_ = [["1st item placeholder"]] - num_fake__fields_ = 1#pytest.gen.randint(1, 10) # arbitrary from 1 to 10 - fake_field_names = [] - - for i in range(num_fake__fields_): - fake_attr_name = "fake_attr_name" - fake_attr_value = MagicMock() - fake_field_names.append(fake_attr_name) - fake__fields_.append([fake_attr_name, fake_attr_value]) - - data_struct._fields_ = fake__fields_ - sbn_adapter.msgID_lookup_table[msgID][1].__setattr__('_fields_', fake__fields_) - - for field_name, field_type in data_struct._fields_[1:]: - expected_current_data[x]['headers'].append('{}.{}.{}'.format(app_name, struct_name, str(field_name))) - - expected_current_data[x]['data'].extend([0]*num_fake__fields_) #initialize all the data arrays with zero - - # Renew abn_adapter and AdapterDataSource to ensure test independence and get field updates to message_headers module - reload(sbn_adapter) - AdapterDataSource = sbn_adapter.AdapterDataSource - - # Act - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Assert - assert cut.currentData == expected_current_data - -# tests for AdapterDataSource.connect -def test_sbn_adapter_AdapterDataSource_connect_when_msgID_lookup_table_has_zero_keys(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - fake_listener_thread = MagicMock() - fake_msgID_lookup_table = MagicMock() - fake_msgID_lookup_table_keys = [] - - mocker.patch(sbn_adapter.__name__ + '.time.sleep') - mocker.patch(sbn_adapter.__name__ + '.os.chdir') - mocker.patch(sbn_adapter.__name__ + '.sbn.sbn_load_and_init') - mocker.patch(sbn_adapter.__name__ + '.message_listener_thread', fake_listener_thread) - mocker.patch(sbn_adapter.__name__ + '.threading.Thread', return_value=fake_listener_thread) - mocker.patch.object(fake_listener_thread, 'start') - mocker.patch(sbn_adapter.__name__ + '.msgID_lookup_table', fake_msgID_lookup_table) - mocker.patch.object(fake_msgID_lookup_table, 'keys', return_value=fake_msgID_lookup_table_keys) - mocker.patch(sbn_adapter.__name__ + '.sbn.subscribe') - - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - cut.connect() - - # Assert - assert sbn_adapter.time.sleep.call_count == 1 - assert sbn_adapter.time.sleep.call_args_list[0].args == (2,) - assert sbn_adapter.os.chdir.call_count == 1 - assert sbn_adapter.os.chdir.call_args_list[0].args == ('cf',) - assert sbn_adapter.sbn.sbn_load_and_init.call_count == 1 - - assert sbn_adapter.threading.Thread.call_count == 1 - assert sbn_adapter.threading.Thread.call_args_list[0].args == () - assert sbn_adapter.threading.Thread.call_args_list[0].kwargs == {'target' : fake_listener_thread} - assert fake_listener_thread.start.call_count == 1 - - assert fake_msgID_lookup_table.keys.call_count == 1 - assert sbn_adapter.sbn.subscribe.call_count == 0 - -def test_sbn_adapter_AdapterDataSource_connect_when_msgID_lookup_table_has_one_key(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - fake_listener_thread = MagicMock() - fake_msgID_lookup_table = MagicMock() - fake_msgID = MagicMock() - fake_msgID_lookup_table_keys = [fake_msgID] - - mocker.patch(sbn_adapter.__name__ + '.time.sleep') - mocker.patch(sbn_adapter.__name__ + '.os.chdir') - mocker.patch(sbn_adapter.__name__ + '.sbn.sbn_load_and_init') - mocker.patch(sbn_adapter.__name__ + '.message_listener_thread', fake_listener_thread) - mocker.patch(sbn_adapter.__name__ + '.threading.Thread', return_value=fake_listener_thread) - mocker.patch.object(fake_listener_thread, 'start') - mocker.patch(sbn_adapter.__name__ + '.msgID_lookup_table', fake_msgID_lookup_table) - mocker.patch.object(fake_msgID_lookup_table, 'keys', return_value=fake_msgID_lookup_table_keys) - mocker.patch(sbn_adapter.__name__ + '.sbn.subscribe') - - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - cut.connect() - - # Assert - assert sbn_adapter.time.sleep.call_count == 1 - assert sbn_adapter.time.sleep.call_args_list[0].args == (2,) - assert sbn_adapter.os.chdir.call_count == 1 - assert sbn_adapter.os.chdir.call_args_list[0].args == ('cf',) - assert sbn_adapter.sbn.sbn_load_and_init.call_count == 1 - - assert sbn_adapter.threading.Thread.call_count == 1 - assert sbn_adapter.threading.Thread.call_args_list[0].args == () - assert sbn_adapter.threading.Thread.call_args_list[0].kwargs == {'target' : fake_listener_thread} - assert fake_listener_thread.start.call_count == 1 - - assert fake_msgID_lookup_table.keys.call_count == 1 - assert sbn_adapter.sbn.subscribe.call_count == 1 - assert sbn_adapter.sbn.subscribe.call_args_list[0].args == (fake_msgID,) - -def test_sbn_adapter_AdapterDataSource_connect_when_msgID_lookup_table_has_multiple_keys(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - fake_listener_thread = MagicMock() - fake_msgID_lookup_table = MagicMock() - num_keys = pytest.gen.randint(2, 10) # arbitrary, from 2 to 10 - fake_msgID_lookup_table_keys = [] - for i in range(num_keys): - fake_msgID_lookup_table_keys.append(MagicMock()) - - mocker.patch(sbn_adapter.__name__ + '.time.sleep') - mocker.patch(sbn_adapter.__name__ + '.os.chdir') - mocker.patch(sbn_adapter.__name__ + '.sbn.sbn_load_and_init') - mocker.patch(sbn_adapter.__name__ + '.message_listener_thread', fake_listener_thread) - mocker.patch(sbn_adapter.__name__ + '.threading.Thread', return_value=fake_listener_thread) - mocker.patch.object(fake_listener_thread, 'start') - mocker.patch(sbn_adapter.__name__ + '.msgID_lookup_table', fake_msgID_lookup_table) - mocker.patch.object(fake_msgID_lookup_table, 'keys', return_value=fake_msgID_lookup_table_keys) - mocker.patch(sbn_adapter.__name__ + '.sbn.subscribe') - - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - cut.connect() - - # Assert - assert sbn_adapter.time.sleep.call_count == 1 - assert sbn_adapter.time.sleep.call_args_list[0].args == (2,) - assert sbn_adapter.os.chdir.call_count == 1 - assert sbn_adapter.os.chdir.call_args_list[0].args == ('cf',) - assert sbn_adapter.sbn.sbn_load_and_init.call_count == 1 - - assert sbn_adapter.threading.Thread.call_count == 1 - assert sbn_adapter.threading.Thread.call_args_list[0].args == () - assert sbn_adapter.threading.Thread.call_args_list[0].kwargs == {'target' : fake_listener_thread} - assert fake_listener_thread.start.call_count == 1 - - assert fake_msgID_lookup_table.keys.call_count == 1 - assert sbn_adapter.sbn.subscribe.call_count == num_keys - for i in range(num_keys): - assert sbn_adapter.sbn.subscribe.call_args_list[i].args == (fake_msgID_lookup_table_keys[i],) - -# tests for AdapterDataSource.subscribe_message -def test_sbn_adapter_AdapterDataSource_subscribe_message_when_msgid_is_not_a_list(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - arg_msgid = MagicMock() - - mocker.patch(sbn_adapter.__name__ + '.sbn.subscribe') - - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - cut.subscribe_message(arg_msgid) - - # Assert - assert sbn_adapter.sbn.subscribe.call_count == 1 - assert sbn_adapter.sbn.subscribe.call_args_list[0].args == (arg_msgid,) - -def test_sbn_adapter_AdapterDataSource_subscribe_message_when_msgid_is_an_empty_list(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - arg_msgid = [] - - mocker.patch(sbn_adapter.__name__ + '.sbn.subscribe') - - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - cut.subscribe_message(arg_msgid) - - # Assert - assert sbn_adapter.sbn.subscribe.call_count == 0 - -def test_sbn_adapter_AdapterDataSource_subscribe_message_when_msgid_is_a_list_with_only_one_element(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - arg_msgid = [MagicMock()] - - mocker.patch(sbn_adapter.__name__ + '.sbn.subscribe') - - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - cut.subscribe_message(arg_msgid) - - # Assert - assert sbn_adapter.sbn.subscribe.call_count == 1 - assert sbn_adapter.sbn.subscribe.call_args_list[0].args == (arg_msgid[0],) - -def test_sbn_adapter_AdapterDataSource_subscribe_message_when_msgid_is_a_list_of_multiple_elements(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - list_length = pytest.gen.randint(2,10) # arbitrary, from 2 to 10 - arg_msgid = [] - for i in range(list_length): - arg_msgid.append(MagicMock()) - - mocker.patch(sbn_adapter.__name__ + '.sbn.subscribe') - - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - cut.subscribe_message(arg_msgid) - - # Assert - assert sbn_adapter.sbn.subscribe.call_count == list_length - for i in range(list_length): - assert sbn_adapter.sbn.subscribe.call_args_list[i].args == (arg_msgid[i],) - -# tests for AdapterDataSource.get_next -def test_sbn_adapter_AdapterDataSource_get_next_when_new_data_is_true(setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - cut = AdapterDataSource.__new__(AdapterDataSource) - AdapterDataSource.new_data = True - pre_call_index = AdapterDataSource.double_buffer_read_index - - # Act - result = cut.get_next() - - # Assert - assert AdapterDataSource.new_data == False - if pre_call_index == 0: - assert AdapterDataSource.double_buffer_read_index == 1 - assert result == AdapterDataSource.currentData[1]['data'] - elif pre_call_index == 1: - assert AdapterDataSource.double_buffer_read_index == 0 - assert result == AdapterDataSource.currentData[0]['data'] - else: - assert False - -def test_sbn_adapter_AdapterDataSource_get_next_when_called_multiple_times_when_new_data_is_true(setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - cut = AdapterDataSource.__new__(AdapterDataSource) - pre_call_index = AdapterDataSource.double_buffer_read_index - - # Act - results = [] - num_calls = pytest.gen.randint(2,10) # arbitrary, 2 to 10 - for i in range(num_calls): - AdapterDataSource.new_data = True - results.append(cut.get_next()) - - # Assert - assert AdapterDataSource.new_data == False - for i in range(num_calls): - results[i] = AdapterDataSource.currentData[pre_call_index]['data'] - pre_call_index = (pre_call_index + 1) % 2 - assert AdapterDataSource.double_buffer_read_index == pre_call_index - -def test_sbn_adapter_AdapterDataSource_get_next_behavior_when_new_data_is_false_then_true(mocker, setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - cut = AdapterDataSource.__new__(AdapterDataSource) - pre_call_index = AdapterDataSource.double_buffer_read_index - - num_falses = pytest.gen.randint(1, 10) - side_effect_list = [False] * num_falses - side_effect_list.append(True) - - AdapterDataSource.new_data = PropertyMock(side_effect=side_effect_list) - mocker.patch(sbn_adapter.__name__ + '.time.sleep') - - # Act - result = cut.get_next() - - # Assert - assert sbn_adapter.time.sleep.call_count == num_falses - assert AdapterDataSource.new_data == False - if pre_call_index == 0: - assert AdapterDataSource.double_buffer_read_index == 1 - assert result == AdapterDataSource.currentData[1]['data'] - elif pre_call_index == 1: - assert AdapterDataSource.double_buffer_read_index == 0 - assert result == AdapterDataSource.currentData[0]['data'] - else: - assert False - -# tests for AdapterDataSource.has_more -def test_sbn_adapter_AdapterDataSource_has_more_returns_true(setup_teardown): - # Arrange - # Renew AdapterDataSource to ensure test independence - AdapterDataSource = sbn_adapter.AdapterDataSource - cut = AdapterDataSource.__new__(AdapterDataSource) - - # Act - result = cut.has_more() - - # Assert - assert result == True \ No newline at end of file From 0e1b79f0f763c813de976ed59cc7ec4889560596 Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Fri, 22 Sep 2023 14:16:28 -0400 Subject: [PATCH 8/9] Remove SBN flag --- onair/config/default_config.ini | 1 - onair/src/run_scripts/execution_engine.py | 5 +- onair/src/run_scripts/sim.py | 15 +-- .../src/run_scripts/test_execution_engine.py | 19 ++-- test/onair/src/run_scripts/test_sim.py | 102 +----------------- 5 files changed, 11 insertions(+), 131 deletions(-) diff --git a/onair/config/default_config.ini b/onair/config/default_config.ini index c27b3fd8..aede9013 100644 --- a/onair/config/default_config.ini +++ b/onair/config/default_config.ini @@ -11,5 +11,4 @@ PluginList = {'generic_plugin':'plugins/generic/generic_plugin.py'} [RUN_FLAGS] IO_Flag = true Dev_Flag = false -SBN_Flag = false Viz_Flag = false diff --git a/onair/src/run_scripts/execution_engine.py b/onair/src/run_scripts/execution_engine.py index 437c92ad..17ef2e43 100644 --- a/onair/src/run_scripts/execution_engine.py +++ b/onair/src/run_scripts/execution_engine.py @@ -30,7 +30,6 @@ def __init__(self, config_file='', run_name='', save_flag=False): # Init Flags self.IO_Flag = False self.Dev_Flag = False - self.SBN_Flag = False self.Viz_Flag = False # Init Paths @@ -101,7 +100,6 @@ def parse_configs(self, config_filepath): ## Parse Optional Data: Flags self.IO_Flag = config['RUN_FLAGS'].getboolean('IO_Flag') self.Dev_Flag = config['RUN_FLAGS'].getboolean('Dev_Flag') - self.SBN_Flag = config['RUN_FLAGS'].getboolean('SBN_Flag') self.Viz_Flag = config['RUN_FLAGS'].getboolean('Viz_Flag') ## Parse Optional Data: Benchmarks @@ -115,12 +113,11 @@ def parse_configs(self, config_filepath): def parse_data(self, parser_name, parser_file_name, data_file_name, metadata_file_name, subsystems_breakdown=False): parser = importlib.import_module('onair.data_handling.' + parser_file_name) parser_class = getattr(parser, parser_name) # This could be simplified if the parsers all extend a parser class... but this works for now - # TODO: This will be changed on an OnAIR Data Source data_parser = parser_class(data_file_name, metadata_file_name, subsystems_breakdown) self.simDataParser = data_parser def setup_sim(self): - self.sim = Simulator(self.sim_name, self.simDataParser, self.plugin_list, self.SBN_Flag) + self.sim = Simulator(self.sim_name, self.simDataParser, self.plugin_list) try: fls = ast.literal_eval(self.benchmarkFiles) fp = os.path.dirname(os.path.realpath(__file__)) + '/../..' + self.benchmarkFilePath diff --git a/onair/src/run_scripts/sim.py b/onair/src/run_scripts/sim.py index 22299273..c3f3b37f 100644 --- a/onair/src/run_scripts/sim.py +++ b/onair/src/run_scripts/sim.py @@ -24,20 +24,9 @@ DIAGNOSIS_INTERVAL = 100 class Simulator: - def __init__(self, simType, dataParser, plugin_list, SBN_Flag): + def __init__(self, simType, dataParser, plugin_list): self.simulator = simType - - if SBN_Flag: - # TODO: This is ugly, but sbn_client is only available when built for cFS... - # ...from sbn_adapter import AdapterDataSource - sbn_adapter = importlib.import_module('onair.src.run_scripts.sbn_adapter') - AdapterDataSource = getattr(sbn_adapter, 'AdapterDataSource') - self.simData = AdapterDataSource(dataParser.get_just_data()) - self.simData.connect() # this also subscribes to the msgIDs - - else: - #self.simData = DataSource(dataParser.get_just_data()) - self.simData = dataParser + self.simData = dataParser headers, tests = dataParser.get_vehicle_metadata() vehicle = VehicleRepresentation(headers, tests) diff --git a/test/onair/src/run_scripts/test_execution_engine.py b/test/onair/src/run_scripts/test_execution_engine.py index 31b5320b..7bfb231a 100644 --- a/test/onair/src/run_scripts/test_execution_engine.py +++ b/test/onair/src/run_scripts/test_execution_engine.py @@ -35,7 +35,6 @@ def test_ExecutionEngine__init__sets_expected_values_but_does_no_calls_when_conf assert cut.run_name == arg_run_name assert cut.IO_Flag == False assert cut.Dev_Flag == False - assert cut.SBN_Flag == False assert cut.Viz_Flag == False assert cut.dataFilePath == '' assert cut.telemetryFile == '' @@ -298,7 +297,6 @@ def test_ExecutionEngine_parse_configs_sets_all_items_without_error(mocker): fake_config_read_result.__len__.return_value = 1 fake_IO_flags = MagicMock() fake_Dev_flags = MagicMock() - fake_SBN_flags = MagicMock() fake_Viz_flags = MagicMock() fake_plugin_dict= MagicMock() fake_keys = MagicMock() @@ -312,7 +310,7 @@ def test_ExecutionEngine_parse_configs_sets_all_items_without_error(mocker): mocker.patch(execution_engine.__name__ + '.configparser.ConfigParser', return_value=fake_config) mocker.patch.object(fake_config, 'read', return_value=fake_config_read_result) - mocker.patch.object(fake_run_flags, 'getboolean', side_effect=[fake_IO_flags, fake_Dev_flags, fake_SBN_flags, fake_Viz_flags]) + mocker.patch.object(fake_run_flags, 'getboolean', side_effect=[fake_IO_flags, fake_Dev_flags, fake_Viz_flags]) mocker.patch.object(cut, 'ast_parse_eval', return_value=fake_plugin_list) mocker.patch(execution_engine.__name__ + '.isinstance', return_value=True) mocker.patch(execution_engine.__name__ + '.ast.literal_eval', return_value=fake_temp_plugin_list) @@ -338,14 +336,12 @@ def test_ExecutionEngine_parse_configs_sets_all_items_without_error(mocker): assert cut.parser_name == fake_default['ParserName'] assert cut.sim_name == fake_default['SimName'] assert cut.plugin_list == fake_temp_plugin_list - assert fake_run_flags.getboolean.call_count == 4 + assert fake_run_flags.getboolean.call_count == 3 assert fake_run_flags.getboolean.call_args_list[0].args == ('IO_Flag', ) assert cut.IO_Flag == fake_IO_flags assert fake_run_flags.getboolean.call_args_list[1].args == ('Dev_Flag', ) assert cut.Dev_Flag == fake_Dev_flags - assert fake_run_flags.getboolean.call_args_list[2].args == ('SBN_Flag', ) - assert cut.SBN_Flag == fake_SBN_flags - assert fake_run_flags.getboolean.call_args_list[3].args == ('Viz_Flag', ) + assert fake_run_flags.getboolean.call_args_list[2].args == ('Viz_Flag', ) assert cut.Viz_Flag == fake_Viz_flags def test_ExecutionEngine_parse_configs_bypasses_benchmarks_when_access_raises_error(mocker): @@ -370,7 +366,6 @@ def test_ExecutionEngine_parse_configs_bypasses_benchmarks_when_access_raises_er fake_config_read_result.__len__.return_value = 1 fake_IO_flags = MagicMock() fake_Dev_flags = MagicMock() - fake_SBN_flags = MagicMock() fake_Viz_flags = MagicMock() fake_plugin_dict = MagicMock() fake_keys = MagicMock() @@ -384,7 +379,7 @@ def test_ExecutionEngine_parse_configs_bypasses_benchmarks_when_access_raises_er mocker.patch(execution_engine.__name__ + '.configparser.ConfigParser', return_value=fake_config) mocker.patch.object(fake_config, 'read', return_value=fake_config_read_result) - mocker.patch.object(fake_run_flags, 'getboolean', side_effect=[fake_IO_flags, fake_Dev_flags, fake_SBN_flags, fake_Viz_flags]) + mocker.patch.object(fake_run_flags, 'getboolean', side_effect=[fake_IO_flags, fake_Dev_flags, fake_Viz_flags]) mocker.patch('ast.literal_eval',return_value=fake_plugin_dict) mocker.patch.object(fake_plugin_dict, 'keys', return_value=fake_keys) mocker.patch.object(fake_plugin_dict, '__getitem__', return_value=fake_path) @@ -520,7 +515,6 @@ def test_ExecutionEngine_setup_sim_sets_self_sim_to_new_Simulator_and_sets_bench cut = ExecutionEngine.__new__(ExecutionEngine) cut.sim_name = MagicMock() cut.simDataParser = MagicMock() - cut.SBN_Flag = MagicMock() cut.benchmarkFiles = MagicMock() cut.benchmarkFilePath = MagicMock() cut.benchmarkIndices = MagicMock() @@ -546,7 +540,7 @@ def test_ExecutionEngine_setup_sim_sets_self_sim_to_new_Simulator_and_sets_bench # Assert assert execution_engine.Simulator.call_count == 1 - assert execution_engine.Simulator.call_args_list[0].args == (cut.sim_name, cut.simDataParser, cut.plugin_list, cut.SBN_Flag) + assert execution_engine.Simulator.call_args_list[0].args == (cut.sim_name, cut.simDataParser, cut.plugin_list) assert cut.sim == fake_sim assert execution_engine.ast.literal_eval.call_count == 2 assert execution_engine.ast.literal_eval.call_args_list[0].args == (cut.benchmarkFiles, ) @@ -561,7 +555,6 @@ def test_ExecutionEngine_setup_sim_sets_self_sim_to_new_Simulator_but_does_not_s cut = ExecutionEngine.__new__(ExecutionEngine) cut.sim_name = MagicMock() cut.simDataParser = MagicMock() - cut.SBN_Flag = MagicMock() cut.benchmarkFiles = MagicMock() cut.benchmarkFilePath = MagicMock() cut.benchmarkIndices = MagicMock() @@ -586,7 +579,7 @@ def test_ExecutionEngine_setup_sim_sets_self_sim_to_new_Simulator_but_does_not_s # Assert assert execution_engine.Simulator.call_count == 1 - assert execution_engine.Simulator.call_args_list[0].args == (cut.sim_name, cut.simDataParser, cut.plugin_list, cut.SBN_Flag) + assert execution_engine.Simulator.call_args_list[0].args == (cut.sim_name, cut.simDataParser, cut.plugin_list) assert cut.sim == fake_sim assert execution_engine.ast.literal_eval.call_count == 1 assert execution_engine.ast.literal_eval.call_args_list[0].args == (cut.benchmarkFiles, ) diff --git a/test/onair/src/run_scripts/test_sim.py b/test/onair/src/run_scripts/test_sim.py index 58e791fb..2afe33ef 100644 --- a/test/onair/src/run_scripts/test_sim.py +++ b/test/onair/src/run_scripts/test_sim.py @@ -17,63 +17,14 @@ from math import ceil, floor # __init__ tests -def test_Simulator__init__creates_Vehicle_and_AdapterDataSource_from_parsed_data_and_Agent_with_vehicle_when_SBN_Flag_resolves_to_True(mocker): +def test_Simulator__init__creates_Vehicle_and_Agent(mocker): # Arrange arg_simType = MagicMock() arg_dataParser = MagicMock() - arg_SBN_Flag = True if (pytest.gen.randint(0,1) == 0) else MagicMock() - - class FakeDataAdapterSource: - def __init__(self, sim_data): - FakeDataAdapterSource.simData = self - FakeDataAdapterSource.sim_data = sim_data - - def connect(self): - if hasattr(FakeDataAdapterSource, 'connect_call_count'): - FakeDataAdapterSource.connect_call_count += 1 - else: - FakeDataAdapterSource.connect_call_count = 1 - - fake_vehicle_metadata = [MagicMock(), MagicMock()] - fake_vehicle = MagicMock() - fake_sim_data = MagicMock() - fake_sbn_adapter = MagicMock() - fake_simData = MagicMock() - fake_agent = MagicMock() - - cut = Simulator.__new__(Simulator) - - mocker.patch.object(arg_dataParser, 'get_vehicle_metadata', return_value=fake_vehicle_metadata) - mocker.patch(sim.__name__ + '.vehicle', return_value=fake_vehicle) - mocker.patch(sim.__name__ + '.importlib.import_module', return_value=fake_sbn_adapter) - mocker.patch(sim.__name__ + '.getattr', return_value=FakeDataAdapterSource) - mocker.patch.object(arg_dataParser, 'get_just_data', return_value=fake_sim_data) - mocker.patch(sim.__name__ + '.Agent', return_value=fake_agent) - - # Act - cut.__init__(arg_simType, arg_dataParser, arg_SBN_Flag) - - # Assert - assert cut.simulator == arg_simType - assert sim.vehicle.call_count == 1 - assert sim.vehicle.call_args_list[0].args == (fake_vehicle_metadata[0], fake_vehicle_metadata[1], ) - assert FakeDataAdapterSource.simData == cut.simData - assert FakeDataAdapterSource.sim_data == fake_sim_data - assert FakeDataAdapterSource.connect_call_count == 1 - assert sim.Agent.call_count == 1 - assert sim.Agent.call_args_list[0].args == (fake_vehicle, ) - assert cut.agent == fake_agent - -def test_Simulator__init__creates_Vehicle_and_DataSource_from_parsed_data_and_Agent_with_vehicle_when_SBN_Flag_resolves_to_False(mocker): - # Arrange - arg_simType = MagicMock() - arg_dataParser = MagicMock() - arg_SBN_Flag = False if (pytest.gen.randint(0,1) == 0) else None arg_plugin_list = MagicMock() fake_vehicle_metadata = [MagicMock(), MagicMock()] fake_vehicle = MagicMock() - fake_sim_data = MagicMock() fake_simData = MagicMock() fake_agent = MagicMock() @@ -81,11 +32,10 @@ def test_Simulator__init__creates_Vehicle_and_DataSource_from_parsed_data_and_Ag mocker.patch.object(arg_dataParser, 'get_vehicle_metadata', return_value=fake_vehicle_metadata) mocker.patch(sim.__name__ + '.VehicleRepresentation', return_value=fake_vehicle) - mocker.patch.object(arg_dataParser, 'get_just_data', return_value=fake_sim_data) mocker.patch(sim.__name__ + '.Agent', return_value=fake_agent) # Act - cut.__init__(arg_simType, arg_dataParser, arg_plugin_list, arg_SBN_Flag) + cut.__init__(arg_simType, arg_dataParser, arg_plugin_list) # Assert assert cut.simulator == arg_simType @@ -96,54 +46,6 @@ def test_Simulator__init__creates_Vehicle_and_DataSource_from_parsed_data_and_Ag assert sim.Agent.call_args_list[0].args == (fake_vehicle, arg_plugin_list) assert cut.agent == fake_agent -def test_Simulator__init__creates_Vehicle_and_AdapterDataSource_from_parsed_data_and_Agent_with_vehicle_when_SBN_Flag_resolves_to_True(mocker): - # Arrange - arg_simType = MagicMock() - arg_dataParser = MagicMock() - arg_SBN_Flag = True if (pytest.gen.randint(0,1) == 0) else MagicMock() - - class FakeDataAdapterSource: - def __init__(self, sim_data): - FakeDataAdapterSource.simData = self - FakeDataAdapterSource.sim_data = sim_data - - def connect(self): - if hasattr(FakeDataAdapterSource, 'connect_call_count'): - FakeDataAdapterSource.connect_call_count += 1 - else: - FakeDataAdapterSource.connect_call_count = 1 - - fake_vehicle_metadata = [MagicMock(), MagicMock()] - fake_vehicle = MagicMock() - fake_sim_data = MagicMock() - fake_sbn_adapter = MagicMock() - fake_simData = MagicMock() - fake_agent = MagicMock() - fake_plugin_list = MagicMock() - - cut = Simulator.__new__(Simulator) - - mocker.patch.object(arg_dataParser, 'get_vehicle_metadata', return_value=fake_vehicle_metadata) - mocker.patch(sim.__name__ + '.VehicleRepresentation', return_value=fake_vehicle) - mocker.patch(sim.__name__ + '.importlib.import_module', return_value=fake_sbn_adapter) - mocker.patch(sim.__name__ + '.getattr', return_value=FakeDataAdapterSource) - mocker.patch.object(arg_dataParser, 'get_just_data', return_value=fake_sim_data) - mocker.patch(sim.__name__ + '.Agent', return_value=fake_agent) - - # Act - cut.__init__(arg_simType, arg_dataParser, fake_plugin_list, arg_SBN_Flag) - - # Assert - assert cut.simulator == arg_simType - assert sim.VehicleRepresentation.call_count == 1 - assert sim.VehicleRepresentation.call_args_list[0].args == (fake_vehicle_metadata[0], fake_vehicle_metadata[1], ) - assert FakeDataAdapterSource.simData == cut.simData - assert FakeDataAdapterSource.sim_data == fake_sim_data - assert FakeDataAdapterSource.connect_call_count == 1 - assert sim.Agent.call_count == 1 - assert sim.Agent.call_args_list[0].args == (fake_vehicle, fake_plugin_list) - assert cut.agent == fake_agent - # run_sim tests def test_Simulator_run_sim_simData_never_has_more_so_loop_does_not_run_and_diagnosis_list_is_empty_but_filled_with_agent_diagnose_and_returns_last_diagnosis(mocker): # Arrange From 7ac20596c4cc41645441750740671f78105305ff Mon Sep 17 00:00:00 2001 From: "Marshall, James C 459937851" Date: Fri, 22 Sep 2023 14:20:40 -0400 Subject: [PATCH 9/9] Slight cleanup --- onair/data_handling/csv_parser.py | 2 -- onair/data_handling/on_air_data_source.py | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/onair/data_handling/csv_parser.py b/onair/data_handling/csv_parser.py index a5bb00b6..dad32260 100644 --- a/onair/data_handling/csv_parser.py +++ b/onair/data_handling/csv_parser.py @@ -30,8 +30,6 @@ def parse_csv_data(self, data_file): dataset = pd.read_csv(data_file, delimiter=',', header=0, dtype=str) dataset = dataset.loc[:, ~dataset.columns.str.contains('^Unnamed')] - # all_headers = list(dataset.columns.values) - #Initialize the entire data dictionary all_data = [] for index, row in dataset.iterrows(): diff --git a/onair/data_handling/on_air_data_source.py b/onair/data_handling/on_air_data_source.py index ce9a509b..80bdc4b5 100644 --- a/onair/data_handling/on_air_data_source.py +++ b/onair/data_handling/on_air_data_source.py @@ -19,7 +19,6 @@ def __init__(self, data_file, meta_file, ss_breakdown = False): self.raw_data_file = data_file self.meta_data_file = meta_file - self.all_headers = [] self.sim_data = {} self.binning_configs = {} @@ -42,7 +41,7 @@ def parse_meta_data_file(self, meta_data_file, ss_breakdown): @abstractmethod def process_data_file(self, data_file): """ - TODO: Comment + Read data frames from the specified file. """ raise NotImplementedError @@ -53,7 +52,6 @@ def get_next(self): """ raise NotImplementedError - # TODO: has_more and has_data are too confusing @abstractmethod def has_more(self): """