From d8572ae3d0410a4d2a954457f1b43a3f0b523ae2 Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Tue, 17 Oct 2023 17:00:57 -0600 Subject: [PATCH 1/7] config_api.py --- openc3/python/openc3/api/config_api.py | 43 ++++++++++++++++ .../python/openc3/models/tool_config_model.py | 42 ++++++++++++++++ openc3/python/openc3/utilities/local_mode.py | 23 +++++---- openc3/python/test/api/test_config_api.py | 36 +++++++++++++ .../{test_inst.py => sim_tgt_inst.py} | 15 ++++-- .../test_simulated_target_interface.py | 50 +++++-------------- .../test/models/test_tool_config_model.py | 43 ++++++++++++++++ openc3/python/test/test_helper.py | 3 +- 8 files changed, 200 insertions(+), 55 deletions(-) create mode 100644 openc3/python/openc3/api/config_api.py create mode 100644 openc3/python/openc3/models/tool_config_model.py create mode 100644 openc3/python/test/api/test_config_api.py rename openc3/python/test/interfaces/{test_inst.py => sim_tgt_inst.py} (65%) create mode 100644 openc3/python/test/models/test_tool_config_model.py diff --git a/openc3/python/openc3/api/config_api.py b/openc3/python/openc3/api/config_api.py new file mode 100644 index 0000000000..88e9c14af3 --- /dev/null +++ b/openc3/python/openc3/api/config_api.py @@ -0,0 +1,43 @@ +# Copyright 2023 OpenC3, Inc +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc.: + +import json +from openc3.api import WHITELIST +from openc3.environment import OPENC3_SCOPE +from openc3.utilities.authorization import authorize +from openc3.models.tool_config_model import ToolConfigModel + +WHITELIST.extend(["list_configs", "load_config", "save_config", "delete_config"]) + + +def list_configs(tool, scope=OPENC3_SCOPE): + authorize(permission="system", scope=scope) + return ToolConfigModel.list_configs(tool, scope=scope) + + +def load_config(tool, name, scope=OPENC3_SCOPE): + authorize(permission="system", scope=scope) + return json.loads(ToolConfigModel.load_config(tool, name, scope=scope)) + + +def save_config(tool, name, data, local_mode=True, scope=OPENC3_SCOPE): + authorize(permission="system_set", scope=scope) + ToolConfigModel.save_config(tool, name, data, local_mode, scope) + + +def delete_config(tool, name, local_mode=True, scope=OPENC3_SCOPE): + authorize(permission="system_set", scope=scope) + ToolConfigModel.delete_config(tool, name, local_mode, scope) diff --git a/openc3/python/openc3/models/tool_config_model.py b/openc3/python/openc3/models/tool_config_model.py new file mode 100644 index 0000000000..e33d54620a --- /dev/null +++ b/openc3/python/openc3/models/tool_config_model.py @@ -0,0 +1,42 @@ +# Copyright 2023 OpenC3, Inc. +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc. + +from openc3.utilities.store import Store +from openc3.utilities.local_mode import LocalMode +from openc3.environment import OPENC3_SCOPE + + +class ToolConfigModel: + @classmethod + def list_configs(cls, tool, scope=OPENC3_SCOPE): + keys = Store.hkeys(f"{scope}__config__{tool}") + return [key.decode() for key in keys] + + @classmethod + def load_config(cls, tool, name, scope=OPENC3_SCOPE): + return Store.hget(f"{scope}__config__{tool}", name).decode() + + @classmethod + def save_config(cls, tool, name, data, local_mode=True, scope=OPENC3_SCOPE): + Store.hset(f"{scope}__config__{tool}", name, data) + if local_mode: + LocalMode.save_tool_config(scope, tool, name, data) + + @classmethod + def delete_config(cls, tool, name, local_mode=True, scope=OPENC3_SCOPE): + Store.hdel(f"{scope}__config__{tool}", name) + if local_mode: + LocalMode.delete_tool_config(scope, tool, name) diff --git a/openc3/python/openc3/utilities/local_mode.py b/openc3/python/openc3/utilities/local_mode.py index 516e9054c5..547ccf7d42 100644 --- a/openc3/python/openc3/utilities/local_mode.py +++ b/openc3/python/openc3/utilities/local_mode.py @@ -15,6 +15,7 @@ # if purchased from OpenC3, Inc. import os +import json from openc3.environment import OPENC3_LOCAL_MODE_PATH @@ -325,7 +326,7 @@ class LocalMode: @classmethod def put_target_file(cls, path, io_or_string, scope): - full_folder_path = f"{OPENC3_LOCAL_MODE_PATH}/{path}" + full_folder_path = f"{cls.LOCAL_MODE_PATH}/{path}" os.makedirs(os.path.dirname(full_folder_path), exist_ok=True) flags = "w" if type(io_or_string) == bytes: @@ -383,17 +384,17 @@ def open_local_file(cls, path, scope): # except: JSON:'P'arserError : error # puts "Unable to initialize tool config due to {error.message}" - # @classmethod - # def save_tool_config(cls, scope, tool, name, data): - # json = json.loads(data) - # config_path = "{OPENC3_LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json" - # FileUtils.mkdir_p(File.dirname(config_path)) - # File.open(config_path, 'w') do |file| - # file.write(JSON.pretty_generate(json)) + @classmethod + def save_tool_config(cls, scope, tool, name, data): + json_data = json.loads(data) + config_path = f"{cls.LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json" + os.makedirs(os.path.dirname(config_path), exist_ok=True) + with open(config_path, "w") as file: + file.write(json.dumps(json_data, indent=2)) - # @classmethod - # def delete_tool_config(cls, scope, tool, name): - # FileUtils.rm_f("{OPENC3_LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json") + @classmethod + def delete_tool_config(cls, scope, tool, name): + os.remove(f"{cls.LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json") # @classmethod # def sync_settings(cls, ): diff --git a/openc3/python/test/api/test_config_api.py b/openc3/python/test/api/test_config_api.py new file mode 100644 index 0000000000..ee280f874b --- /dev/null +++ b/openc3/python/test/api/test_config_api.py @@ -0,0 +1,36 @@ +# Copyright 2023 OpenC3, Inc. +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc. + +import unittest +from unittest.mock import * +from test.test_helper import * +from openc3.api.config_api import * + + +class TestConfigApi(unittest.TestCase): + def setUp(self): + mock_redis(self) + + def test_save_load_list_delete(self): + data = {"key": "value", "longer key": "more data"} + save_config("TestConfigApiTool", "config", json.dumps(data), local_mode=False) + configs = list_configs("TestConfigApiTool") + self.assertEqual(configs, ["config"]) + config = load_config("TestConfigApiTool", "config") + self.assertEqual(data, config) + delete_config("TestConfigApiTool", "config", local_mode=False) + configs = list_configs("TestConfigApiTool") + self.assertEqual(configs, []) diff --git a/openc3/python/test/interfaces/test_inst.py b/openc3/python/test/interfaces/sim_tgt_inst.py similarity index 65% rename from openc3/python/test/interfaces/test_inst.py rename to openc3/python/test/interfaces/sim_tgt_inst.py index b2afacbf07..478fac82be 100644 --- a/openc3/python/test/interfaces/test_inst.py +++ b/openc3/python/test/interfaces/sim_tgt_inst.py @@ -1,16 +1,21 @@ - from openc3.utilities.simulated_target import SimulatedTarget from openc3.packets.packet import Packet -class TestInst(SimulatedTarget): + + +class SimTgtInst(SimulatedTarget): packet = None + def __init__(self, target): super().__init__(target) + def set_rates(self): pass + def write(self, packet): - TestInst.packet = packet + SimTgtInst.packet = packet + def read(self, count, time): pkts = [] - pkts.append(Packet("INST","ADCS")) - pkts.append(Packet("INST","HEALTH_STATUS")) + pkts.append(Packet("INST", "ADCS")) + pkts.append(Packet("INST", "HEALTH_STATUS")) return pkts diff --git a/openc3/python/test/interfaces/test_simulated_target_interface.py b/openc3/python/test/interfaces/test_simulated_target_interface.py index 7279d1ef29..1780372772 100644 --- a/openc3/python/test/interfaces/test_simulated_target_interface.py +++ b/openc3/python/test/interfaces/test_simulated_target_interface.py @@ -37,36 +37,10 @@ def read_packet(self, packet): class TestSimulatedTargetInterface(unittest.TestCase): - @classmethod - def setUpClass(cls): + def setUp(self): + mock_redis(self) setup_system() - filename = os.path.join(os.path.dirname(__file__), "test_inst.py") - with open(filename, "w") as file: - file.write( - """ -from openc3.utilities.simulated_target import SimulatedTarget -from openc3.packets.packet import Packet -class TestInst(SimulatedTarget): - packet = None - def __init__(self, target): - super().__init__(target) - def set_rates(self): - pass - def write(self, packet): - TestInst.packet = packet - def read(self, count, time): - pkts = [] - pkts.append(Packet("INST","ADCS")) - pkts.append(Packet("INST","HEALTH_STATUS")) - return pkts -""" - ) - - # @classmethod - # def tearDownClass(cls): - # os.remove(os.path.join(os.path.dirname(__file__), "test_inst.py")) - def test_complains_if_the_simulated_target_file_doesnt_exist(self): with self.assertRaisesRegex( ModuleNotFoundError, "No module named 'doesnt_exist'" @@ -74,11 +48,11 @@ def test_complains_if_the_simulated_target_file_doesnt_exist(self): SimulatedTargetInterface("doesnt_exist.py") def test_creates_the_simulated_target_class(self): - si = SimulatedTargetInterface("test/interfaces/test_inst.py") - self.assertEqual(si.sim_target_class.__name__, "TestInst") + si = SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py") + self.assertEqual(si.sim_target_class.__name__, "SimTgtInst") def test_connects_the_simulated_target(self): - sti = SimulatedTargetInterface("test/interfaces/test_inst.py") + sti = SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py") sti.target_names = ["INST"] self.assertFalse(sti.connected()) sti.connect() @@ -86,10 +60,10 @@ def test_connects_the_simulated_target(self): def test_read_complains_if_disconnected(self): with self.assertRaisesRegex(RuntimeError, "Interface not connected"): - SimulatedTargetInterface("test/interfaces/test_inst.py").read() + SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py").read() def test_read_returns_a_simulated_packet(self): - sti = SimulatedTargetInterface("test/interfaces/test_inst.py") + sti = SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py") sti.target_names = ["INST"] sti.connect() pkt = sti.read() @@ -108,10 +82,10 @@ def test_writes_into_a_pkt(self): def test_write_complains_if_disconnected(self): with self.assertRaisesRegex(RuntimeError, "Interface not connected"): - SimulatedTargetInterface("test/interfaces/test_inst.py").write(None) + SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py").write(None) def test_writes_commands_to_the_simulator(self): - sti = SimulatedTargetInterface("test/interfaces/test_inst.py") + sti = SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py") sti.target_names = ["INST"] sti.connect() sti.write(Packet("INST", "ABORT")) @@ -120,10 +94,10 @@ def test_writes_commands_to_the_simulator(self): def test_write_raw_raises_an_exception(self): with self.assertRaisesRegex(RuntimeError, "not implemented"): - SimulatedTargetInterface("test/interfaces/test_inst.py").write_raw("") + SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py").write_raw("") def test_disconnects_from_the_simulator(self): - sti = SimulatedTargetInterface("test/interfaces/test_inst.py") + sti = SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py") sti.target_names = ["INST"] self.assertFalse(sti.connected()) sti.connect() @@ -132,7 +106,7 @@ def test_disconnects_from_the_simulator(self): self.assertFalse(sti.connected()) def test_handles_packet_protocols(self): - sti = SimulatedTargetInterface("test/interfaces/test_inst.py") + sti = SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py") sti.target_names = ["INST"] sti.add_protocol(TestProtocol, [], "READ_WRITE") sti.connect() diff --git a/openc3/python/test/models/test_tool_config_model.py b/openc3/python/test/models/test_tool_config_model.py new file mode 100644 index 0000000000..666cb56649 --- /dev/null +++ b/openc3/python/test/models/test_tool_config_model.py @@ -0,0 +1,43 @@ +# Copyright 2023 OpenC3, Inc. +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc. + +import json +import unittest +from unittest.mock import * +from test.test_helper import * +from openc3.models.tool_config_model import ToolConfigModel + + +class TestToolConfigModel(unittest.TestCase): + def setUp(self): + mock_redis(self) + + def test_save_load_list_delete(self): + data = {"key": "value", "longer key": "more data"} + ToolConfigModel.save_config( + "TestTool", "config", json.dumps(data), local_mode=False + ) + # self.assertTrue( + # os.path.isfile(f"{TEST_DIR}/DEFAULT/tool_config/TestTool/config.json") + # ) + configs = ToolConfigModel.list_configs("TestTool") + self.assertEqual(configs, ["config"]) + config = ToolConfigModel.load_config("TestTool", "config") + self.assertEqual(config, json.dumps(data)) + ToolConfigModel.delete_config("TestTool", "config", local_mode=False) + # self.assertFalse( + # os.path.isfile(f"{TEST_DIR}/DEFAULT/tool_config/TestTool/config.json") + # ) diff --git a/openc3/python/test/test_helper.py b/openc3/python/test/test_helper.py index 3873a438d9..c0ca726c09 100644 --- a/openc3/python/test/test_helper.py +++ b/openc3/python/test/test_helper.py @@ -21,6 +21,7 @@ os.environ["OPENC3_LOGS_BUCKET"] = "logs" os.environ["OPENC3_TOOLS_BUCKET"] = "tools" os.environ["OPENC3_CONFIG_BUCKET"] = "config" +os.environ["OPENC3_LOCAL_MODE_PATH"] = os.path.dirname(__file__) import io import sys import json @@ -31,7 +32,7 @@ from openc3.utilities.store import Store, EphemeralStore from openc3.system.system import System -TEST_DIR = os.path.realpath(__file__) +TEST_DIR = os.path.dirname(__file__) def setup_system(targets=["SYSTEM", "INST", "EMPTY"]): From 958532804120d1dbc314df96f77b5179d8aa82a6 Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Wed, 18 Oct 2023 08:25:13 -0600 Subject: [PATCH 2/7] Add settins_api --- openc3/python/openc3/api/settings_api.py | 59 ++++++++++++++ openc3/python/openc3/models/setting_model.py | 49 ++++++++++++ openc3/python/openc3/utilities/local_mode.py | 13 ++-- openc3/python/test/api/test_settings_api.py | 76 +++++++++++++++++++ .../interfaces/protocols/test_crc_protocol.py | 3 +- openc3/python/test/test_helper.py | 1 + 6 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 openc3/python/openc3/api/settings_api.py create mode 100644 openc3/python/openc3/models/setting_model.py create mode 100644 openc3/python/test/api/test_settings_api.py diff --git a/openc3/python/openc3/api/settings_api.py b/openc3/python/openc3/api/settings_api.py new file mode 100644 index 0000000000..09ddc353fa --- /dev/null +++ b/openc3/python/openc3/api/settings_api.py @@ -0,0 +1,59 @@ +# Copyright 2023 OpenC3, Inc +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc.: + +from openc3.api import WHITELIST +from openc3.environment import OPENC3_SCOPE +from openc3.utilities.authorization import authorize +from openc3.models.setting_model import SettingModel +from openc3.utilities.local_mode import LocalMode + +WHITELIST.extend( + ["list_settings", "get_all_settings", "get_setting", "get_settings", "save_setting"] +) + + +def list_settings(scope=OPENC3_SCOPE): + authorize(permission="system", scope=scope) + return SettingModel.names(scope=scope) + + +def get_all_settings(scope=OPENC3_SCOPE): + authorize(permission="system", scope=scope) + return SettingModel.all(scope=scope) + + +def get_setting(name, scope=OPENC3_SCOPE): + authorize(permission="system", scope=scope) + setting = SettingModel.get(name=name, scope=scope) + if setting: + return setting["data"] + else: + return None + + +def get_settings(*settings, scope=OPENC3_SCOPE): + authorize(permission="system", scope=scope) + result = [] + for name in settings: + result.append(get_setting(name, scope)) + return result + + +def save_setting(name, data, local_mode=True, scope=OPENC3_SCOPE): + authorize(permission="admin", scope=scope) + SettingModel.set({"name": name, "data": data}, scope=scope) + if local_mode: + LocalMode.save_setting(scope, name, data) diff --git a/openc3/python/openc3/models/setting_model.py b/openc3/python/openc3/models/setting_model.py new file mode 100644 index 0000000000..3527dcec4f --- /dev/null +++ b/openc3/python/openc3/models/setting_model.py @@ -0,0 +1,49 @@ +# Copyright 2023 OpenC3, Inc. +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc. + +from openc3.models.model import Model + + +class SettingModel(Model): + PRIMARY_KEY = "openc3__settings" + + # NOTE: The following three class methods are used by the ModelController + # and are reimplemented to enable various Model class methods to work + @classmethod + def get(cls, name, scope=None): + return super().get(SettingModel.PRIMARY_KEY, name=name) + + @classmethod + def names(cls, scope=None): + return super().names(SettingModel.PRIMARY_KEY) + + @classmethod + def all(cls, scope=None): + return super().all(SettingModel.PRIMARY_KEY) + + # END NOTE + + def __init__(self, name, data, scope): + super().__init__(SettingModel.PRIMARY_KEY, name=name, scope=scope) + self.data = data + + # @return [Hash] JSON encoding of this model + def as_json(self): + return { + "name": self.name, + "data": self.data, + "updated_at": self.updated_at, + } diff --git a/openc3/python/openc3/utilities/local_mode.py b/openc3/python/openc3/utilities/local_mode.py index 547ccf7d42..475437ada5 100644 --- a/openc3/python/openc3/utilities/local_mode.py +++ b/openc3/python/openc3/utilities/local_mode.py @@ -407,12 +407,13 @@ def delete_tool_config(cls, scope, tool, name): # data = File.read(config) # SettingModel.set({ name: name, data: data }, scope: scope) - # @classmethod - # def save_setting(cls, scope, name, data): - # config_path = "{OPENC3_LOCAL_MODE_PATH}/{scope}/settings/{name}.json" - # FileUtils.mkdir_p(File.dirname(config_path)) - # # Anything can be stored as a setting so write it out directly - # File.write(config_path, data) + @classmethod + def save_setting(cls, scope, name, data): + config_path = f"{cls.LOCAL_MODE_PATH}/{scope}/settings/{name}.json" + os.makedirs(os.path.dirname(config_path), exist_ok=True) + # Anything can be stored as a setting so write it out directly + with open(config_path, "w") as file: + file.write(str(data)) # # Helper methods diff --git a/openc3/python/test/api/test_settings_api.py b/openc3/python/test/api/test_settings_api.py new file mode 100644 index 0000000000..5358c9060e --- /dev/null +++ b/openc3/python/test/api/test_settings_api.py @@ -0,0 +1,76 @@ +# Copyright 2023 OpenC3, Inc. +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc. + +import unittest +from unittest.mock import * +from test.test_helper import * +from openc3.api.settings_api import * + + +class TestSettingsApi(unittest.TestCase): + def setUp(self): + mock_redis(self) + + def test_sets_a_value_in_the_stash(self): + save_setting("key", "val", local_mode=False) + self.assertEqual(get_setting("key"), "val") + + def test_sets_an_array_in_the_stash(self): + data = [1, 2, [3, 4]] + save_setting("key", data, local_mode=False) + self.assertEqual(get_setting("key"), data) + + def test_sets_a_hash_in_the_stash(self): + data = {"key": "val", "more": 1} + save_setting("key", data, local_mode=False) + self.assertEqual(get_setting("key"), ({"key": "val", "more": 1})) + + def test_get_returns_none_if_the_value_doesnt_exist(self): + self.assertIsNone(get_setting("nope")) + + def test_list_returns_empty_array_with_no_keys(self): + self.assertEqual(list_settings(), ([])) + + def test_returns_all_the_setting_keys_as_an_array(self): + save_setting("key1", "val", local_mode=False) + save_setting("key2", "val", local_mode=False) + save_setting("key3", "val", local_mode=False) + self.assertEqual(list_settings(), ["key1", "key2", "key3"]) + + def test_get_all_returns_empty_hash_with_no_keys(self): + self.assertEqual(get_all_settings(), ({})) + + def test_returns_all_setting_values_as_a_hash(self): + save_setting("key1", 1, local_mode=False) + save_setting("key2", 2, local_mode=False) + save_setting("key3", 3, local_mode=False) + result = {"key1": 1, "key2": 2, "key3": 3} + self.assertEqual(get_all_settings().keys(), result.keys()) + self.assertEqual(get_all_settings()["key1"]["name"], "key1") + self.assertEqual(get_all_settings()["key1"]["data"], 1) + self.assertEqual(get_all_settings()["key2"]["name"], "key2") + self.assertEqual(get_all_settings()["key2"]["data"], 2) + self.assertEqual(get_all_settings()["key3"]["name"], "key3") + self.assertEqual(get_all_settings()["key3"]["data"], 3) + + def test_get_returns_empty_array_with_no_keys(self): + self.assertEqual(get_settings(), ([])) + + def test_returns_specified_settings_as_an_array_of_results(self): + save_setting("key1", "string", local_mode=False) + save_setting("key2", 2, local_mode=False) + save_setting("key3", 3, local_mode=False) + self.assertEqual(get_settings("key1", "key3"), ["string", 3]) diff --git a/openc3/python/test/interfaces/protocols/test_crc_protocol.py b/openc3/python/test/interfaces/protocols/test_crc_protocol.py index e87630031f..1d21d93330 100644 --- a/openc3/python/test/interfaces/protocols/test_crc_protocol.py +++ b/openc3/python/test/interfaces/protocols/test_crc_protocol.py @@ -535,8 +535,7 @@ def test_reads_the_64_bit_crc_field_and_compares_to_the_crc2(self): self.assertEqual(len(packet.buffer), 12) self.assertEqual(packet.buffer, TestCrcProtocol.buffer) - @patch("openc3.utilities.logger.Logger", return_value=Mock()) - def test_logs_an_error_if_the_crc_does_not_match(self, logger): + def test_logs_an_error_if_the_crc_does_not_match(self): self.interface.stream = TestCrcProtocol.CrcStream() self.interface.add_protocol(BurstProtocol, [], "READ_WRITE") self.interface.add_protocol( diff --git a/openc3/python/test/test_helper.py b/openc3/python/test/test_helper.py index c0ca726c09..5306796dc0 100644 --- a/openc3/python/test/test_helper.py +++ b/openc3/python/test/test_helper.py @@ -33,6 +33,7 @@ from openc3.system.system import System TEST_DIR = os.path.dirname(__file__) +Logger.no_store = True def setup_system(targets=["SYSTEM", "INST", "EMPTY"]): From 72947acba0c2b7f0c3dc45d690077abb8653517a Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Wed, 18 Oct 2023 10:23:24 -0600 Subject: [PATCH 3/7] Ruby api_shared coverage --- openc3/lib/openc3/script/api_shared.rb | 36 +++- openc3/spec/script/api_shared_spec.rb | 243 +++++++++++++++++++++++-- 2 files changed, 257 insertions(+), 22 deletions(-) diff --git a/openc3/lib/openc3/script/api_shared.rb b/openc3/lib/openc3/script/api_shared.rb index 1fe01bd314..6d6930bec6 100644 --- a/openc3/lib/openc3/script/api_shared.rb +++ b/openc3/lib/openc3/script/api_shared.rb @@ -306,7 +306,10 @@ def wait_check(*args, type: :CONVERTED, scope: $openc3_scope, token: $openc3_tok success, value = _openc3_script_wait_implementation_comparison(target_name, packet_name, item_name, type, comparison_to_eval, timeout, polling_rate, scope: scope, token: token, &block) value = "'#{value}'" if value.is_a? String # Show user the check against a quoted string time_diff = Time.now.sys - start_time - check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)} #{comparison_to_eval}" + check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)}" + if comparison_to_eval + check_str += " #{comparison_to_eval}" + end with_value_str = "with value == #{value} after waiting #{time_diff} seconds" if success Logger.info "#{check_str} success #{with_value_str}" @@ -545,6 +548,10 @@ def _check_process_args(args, method_name) case args.length when 1 target_name, packet_name, item_name, comparison_to_eval = extract_fields_from_check_text(args[0]) + when 3 + target_name = args[0] + packet_name = args[1] + item_name = args[2] when 4 target_name = args[0] packet_name = args[1] @@ -554,7 +561,9 @@ def _check_process_args(args, method_name) # Invalid number of arguments raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{method_name}()" end - raise "Invalid comparison to non-ascii value" unless comparison_to_eval.is_printable? + if comparison_to_eval and !comparison_to_eval.is_printable? + raise "ERROR: Invalid comparison to non-ascii value" + end return [target_name, packet_name, item_name, comparison_to_eval] end @@ -732,8 +741,9 @@ def _wait_check_process_args(args) def _openc3_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, exp_to_eval, scope: $openc3_scope, token: $openc3_token, &block) end_time = Time.now.sys + timeout - raise "Invalid comparison to non-ascii value" unless exp_to_eval.is_printable? - + if exp_to_eval and !exp_to_eval.is_printable? + raise "ERROR: Invalid comparison to non-ascii value" + end while true work_start = Time.now.sys value = tlm(target_name, packet_name, item_name, type: value_type, scope: scope, token: token) @@ -762,15 +772,23 @@ def _openc3_script_wait_implementation(target_name, packet_name, item_name, valu if canceled value = tlm(target_name, packet_name, item_name, type: value_type, scope: scope, token: token) - begin - if eval(exp_to_eval) + if not block.nil? + if block.call(value) return true, value else return false, value end - # NoMethodError is raised when the tlm() returns nil and we try to eval the expression - rescue NoMethodError - return false, value + else + begin + if eval(exp_to_eval) + return true, value + else + return false, value + end + # NoMethodError is raised when the tlm() returns nil and we try to eval the expression + rescue NoMethodError + return false, value + end end end end diff --git a/openc3/spec/script/api_shared_spec.rb b/openc3/spec/script/api_shared_spec.rb index 01f41fa683..49c188f2c6 100644 --- a/openc3/spec/script/api_shared_spec.rb +++ b/openc3/spec/script/api_shared_spec.rb @@ -140,6 +140,23 @@ def openc3_script_sleep(sleep_time = nil) end describe "check" do + it "raises with invalid params" do + expect { check("INST", "HEALTH_STATUS") }.to raise_error(/ERROR: Invalid number of arguments \(2\) passed to check/) + end + + it "raises when checking against binary" do + expect { check("INST HEALTH_STATUS TEMP1 == \xFF") }.to raise_error(/ERROR: Invalid comparison to non-ascii value/) + end + + it "prints the value with no comparision" do + capture_io do |stdout| + check("INST", "HEALTH_STATUS", "TEMP1") + expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS TEMP1 == 1/) + check("INST HEALTH_STATUS TEMP1", type: :RAW) + expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS TEMP1 == 1/) + end + end + it "checks a telemetry item against a value" do capture_io do |stdout| check("INST", "HEALTH_STATUS", "TEMP1", "> 1") @@ -150,6 +167,15 @@ def openc3_script_sleep(sleep_time = nil) expect { check("INST HEALTH_STATUS TEMP1 > 100") }.to raise_error(/CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10/) end + it "logs instead of raises when disconnected" do + $disconnect = true + capture_io do |stdout| + check("INST HEALTH_STATUS TEMP1 > 100") + expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10/) + end + $disconnect = false + end + it "warns when checking a state against a constant" do capture_io do |stdout| check("INST HEALTH_STATUS CCSDSSHF == 'FALSE'") @@ -159,7 +185,6 @@ def openc3_script_sleep(sleep_time = nil) end end - # DEPRECATED describe "check_raw, check_formatted, check_with_units" do it "checks against the specified type" do capture_io do |stdout| @@ -212,6 +237,10 @@ def openc3_script_sleep(sleep_time = nil) end describe "check_tolerance" do + it "raises with invalid params" do + expect { check_tolerance("INST", "HEALTH_STATUS", 1.55, 0.1, type: :RAW) }.to raise_error(/ERROR: Invalid number of arguments \(4\) passed to check_tolerance/) + end + it "raises with :FORMATTED or :WITH_UNITS" do expect { check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type: :FORMATTED) }.to raise_error("Invalid type 'FORMATTED' for check_tolerance") expect { check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type: :WITH_UNITS) }.to raise_error("Invalid type 'WITH_UNITS' for check_tolerance") @@ -227,6 +256,19 @@ def openc3_script_sleep(sleep_time = nil) expect { check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1) }.to raise_error(CheckError, /CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5/) end + it "logs instead of raises exception when disconnected" do + $disconnect = true + capture_io do |stdout| + check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1) + expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5/) + end + capture_io do |stdout| + check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1) + expect(stdout.string).to match(/INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2/) + end + $disconnect = false + end + it "checks that an array value is within a single tolerance" do capture_io do |stdout| check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1) @@ -246,6 +288,8 @@ def openc3_script_sleep(sleep_time = nil) expect(stdout.string).to include("CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3") expect(stdout.string).to include("CHECK: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4") end + expect { check_tolerance("INST HEALTH_STATUS ARY", [3, 3, 4], 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2/) + expect { check_tolerance("INST HEALTH_STATUS ARY", [1, 2, 3, 4], 0.1) }.to raise_error(/ERROR: Invalid array size for expected_value/) end it "checks that an array value is within multiple tolerances" do @@ -255,6 +299,8 @@ def openc3_script_sleep(sleep_time = nil) expect(stdout.string).to include("CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3") expect(stdout.string).to include("CHECK: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4") end + expect { check_tolerance("INST HEALTH_STATUS ARY", 3, [0.1, 0.1, 2]) }.to raise_error(/INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2/) + expect { check_tolerance("INST HEALTH_STATUS ARY", 3, [0.1, 0.1, 2, 3]) }.to raise_error(/ERROR: Invalid array size for tolerance/) end end @@ -276,6 +322,15 @@ def openc3_script_sleep(sleep_time = nil) expect { check_expression("true == false") }.to raise_error(/CHECK: true == false is FALSE/) end + it "logs instead of raises when disconnected" do + $disconnect = true + capture_io do |stdout| + check_expression("true == false") + expect(stdout.string).to match(/CHECK: true == false is FALSE/) + end + $disconnect = false + end + it "checks a logical expression" do capture_io do |stdout| check_expression("'STRING' == 'STRING'") @@ -326,6 +381,11 @@ def openc3_script_sleep(sleep_time = nil) describe "wait_tolerance" do + it "raises with invalid params" do + expect { wait_tolerance("INST", "HEALTH_STATUS", "TEMP2", type: :RAW) }.to raise_error(/ERROR: Invalid number of arguments \(3\) passed to wait_tolerance/) + expect { wait_tolerance("INST", "HEALTH_STATUS", 1.55, 0.1, type: :RAW) }.to raise_error(/ERROR: Telemetry Item must be specified/) + end + it "raises with :FORMATTED or :WITH_UNITS" do expect { wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type: :FORMATTED) }.to raise_error("Invalid type 'FORMATTED' for wait_tolerance") expect { wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type: :WITH_UNITS) }.to raise_error("Invalid type 'WITH_UNITS' for wait_tolerance") @@ -361,6 +421,11 @@ def openc3_script_sleep(sleep_time = nil) expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2") expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3") expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4") + + wait_tolerance("INST HEALTH_STATUS ARY", [2, 3, 4], 0.1, 5) + expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2") + expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3") + expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4") end end @@ -370,17 +435,27 @@ def openc3_script_sleep(sleep_time = nil) expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2") expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3") expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4") + + wait_tolerance("INST HEALTH_STATUS ARY", 3, [1, 0.1, 2], 5) + expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2") + expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3") + expect(stdout.string).to include("WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4") end end end describe "wait_expression" do - it "waits for an expression" do - capture_io do |stdout| - wait_expression("true == true", 5) - expect(stdout.string).to match(/WAIT: true == true is TRUE after waiting .* seconds/) - wait_expression("true == false", 0.1) - expect(stdout.string).to match(/WAIT: true == false is FALSE after waiting .* seconds/) + [true, false].each do |cancel| + context "with wait cancelled #{cancel}" do + it "waits for an expression" do + @sleep_cancel = cancel + capture_io do |stdout| + wait_expression("true == true", 5) + expect(stdout.string).to match(/WAIT: true == true is TRUE after waiting .* seconds/) + wait_expression("true == false", 0.1) + expect(stdout.string).to match(/WAIT: true == false is FALSE after waiting .* seconds/) + end + end end end @@ -396,25 +471,59 @@ def openc3_script_sleep(sleep_time = nil) end describe "wait_check" do + it "raises with invalid params" do + expect { wait_check("INST HEALTH_STATUS TEMP1") }.to raise_error(/ERROR: Invalid number of arguments \(1\) passed to wait_check/) + end + it "checks a telemetry item against a value" do capture_io do |stdout| - wait_check("INST", "HEALTH_STATUS", "TEMP1", "> 1", 0.01) - expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS TEMP1 > 1 success with value == 10/) - wait_check("INST HEALTH_STATUS TEMP1 == 1", 0.01, type: :RAW) - expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS TEMP1 == 1 success with value == 1/) + wait_check("INST", "HEALTH_STATUS", "TEMP1", "> 1", 0.01, 0.1) # Last param is polling rate + expect(stdout.string).to include('CHECK: INST HEALTH_STATUS TEMP1 > 1 success with value == 10') + wait_check("INST HEALTH_STATUS TEMP1 == 1", 0.01, 0.1, type: :RAW) + expect(stdout.string).to include('CHECK: INST HEALTH_STATUS TEMP1 == 1 success with value == 1') end expect { wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) }.to raise_error(/CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10/) end + [true, false].each do |cancel| + context "with wait cancelled #{cancel}" do + it "handles a block" do + @sleep_cancel = cancel + + capture_io do |stdout| + wait_check("INST HEALTH_STATUS TEMP1", 0.01) do |value| + value == 10 + end + expect(stdout.string).to include('CHECK: INST HEALTH_STATUS TEMP1 success with value == 10') + end + + expect { + wait_check("INST HEALTH_STATUS TEMP1", 0.01) do |value| + value == 1 + end + }.to raise_error(/CHECK: INST HEALTH_STATUS TEMP1 failed with value == 10/) + end + end + end + + it "logs instead of raises when disconnected" do + $disconnect = true + capture_io do |stdout| + wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) + expect(stdout.string).to include('CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10') + end + $disconnect = false + end + it "fails against binary data" do data = "\xFF" * 10 - expect { wait_check("INST HEALTH_STATUS BLOCKTEST == '#{data}'", 0.01) }.to raise_error(RuntimeError, "Invalid comparison to non-ascii value") + expect { wait_check("INST HEALTH_STATUS BLOCKTEST == '#{data}'", 0.01) }.to raise_error(RuntimeError, "ERROR: Invalid comparison to non-ascii value") end it "warns when checking a state against a constant" do capture_io do |stdout| wait_check("INST HEALTH_STATUS CCSDSSHF == 'FALSE'", 0.01) - expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS CCSDSSHF == 'FALSE' success with value == 'FALSE'/) + expect(stdout.string).to include("CHECK: INST HEALTH_STATUS CCSDSSHF == 'FALSE' success with value == 'FALSE'") end expect { wait_check("INST HEALTH_STATUS CCSDSSHF == FALSE", 0.01) }.to raise_error(NameError, "Uninitialized constant FALSE. Did you mean 'FALSE' as a string?") end @@ -448,6 +557,19 @@ def openc3_script_sleep(sleep_time = nil) expect { wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[2\] failed to be within range 2.9 to 3.1 with value == 4/) end + it "logs instead of raises when disconnected" do + $disconnect = true + capture_io do |stdout| + wait_check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) + expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5/) + end + capture_io do |stdout| + wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) + expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2/) + end + $disconnect = false + end + it "checks that multiple array values are within tolerance" do capture_io do |stdout| wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1, 5) @@ -476,6 +598,15 @@ def openc3_script_sleep(sleep_time = nil) expect { wait_check_expression("true == false", 0.1) }.to raise_error(/CHECK: true == false is FALSE/) end + it "logs instead of raises when disconnected" do + $disconnect = true + capture_io do |stdout| + wait_check_expression("true == false", 5) + expect(stdout.string).to match(/CHECK: true == false is FALSE/) + end + $disconnect = false + end + it "waits and checks a logical expression" do capture_io do |stdout| wait_check_expression("'STRING' == 'STRING'", 5) @@ -525,6 +656,16 @@ def openc3_script_sleep(sleep_time = nil) expect { wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) }.to raise_error(/CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times/) end + it "logs instead of raises if disconnected" do + $disconnect = true + @count = false + capture_io do |stdout| + wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) + expect(stdout.string).to match(/CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times/) + end + $disconnect = false + end + it "prints success if the packet is received" do @count = true capture_io do |stdout| @@ -539,5 +680,81 @@ def openc3_script_sleep(sleep_time = nil) end end end + + describe "disable_instrumentation" do + it "does nothing if RunningScript is not defined" do + capture_io do |stdout| + disable_instrumentation do + puts "HI" + end + expect(stdout.string).to match("HI") + end + end + + it "sets RunningScript.instance.use_instrumentation" do + class RunningScript + @struct = OpenStruct.new + @struct.use_instrumentation = true + def self.instance + @struct + end + end + capture_io do |stdout| + expect(RunningScript.instance.use_instrumentation).to be true + disable_instrumentation do + expect(RunningScript.instance.use_instrumentation).to be false + puts "HI" + end + expect(RunningScript.instance.use_instrumentation).to be true + expect(stdout.string).to match("HI") + end + end + end + + describe "get_line_delay, set_line_delay" do + it "gets and sets RunningScript.line_delay" do + class RunningScript + instance_attr_accessor :line_delay + end + + set_line_delay(10) + expect(RunningScript.line_delay).to eql 10 + expect(get_line_delay()).to eql 10 + end + end + + describe "get_max_output, set_max_output" do + it "gets and sets RunningScript.max_output_characters" do + class RunningScript + instance_attr_accessor :max_output_characters + end + + set_max_output(100) + expect(RunningScript.max_output_characters).to eql 100 + expect(get_max_output()).to eql 100 + end + end + + describe "start, load_utility, require_utility" do + it "loads a script" do + File.open("tester.rb", 'w') do |file| + file.puts "# Nothing" + end + + start("tester.rb") + load_utility("tester.rb") + result = require_utility("tester") + expect(result).to be_truthy() + result = require_utility("tester") + expect(result).to be false + + File.delete('tester.rb') + + expect { load_utility('tester.rb') }.to raise_error(LoadError) + expect { start('tester.rb') }.to raise_error(LoadError) + # Can't try tester.rb because it's already been loaded and cached + expect { require_utility('does_not_exist.rb') }.to raise_error(LoadError) + end + end end end From 49721e5e2710c7dfb37e9e7a2f1ecf6dcfe61b95 Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Thu, 19 Oct 2023 22:34:34 -0600 Subject: [PATCH 4/7] Add api_shared python testing --- openc3/lib/openc3/api/config_api.rb | 10 +- openc3/python/openc3/script/api_shared.py | 166 ++--- openc3/python/ruby_to_python.py | 2 + .../test_simulated_target_interface.py | 1 - openc3/python/test/script/test_api_shared.py | 675 ++++++++++++++++-- playwright/tests/admin/settings.spec.ts | 5 +- playwright/tests/autonomic.spec.ts | 20 +- playwright/tests/bucket-explorer.spec.ts | 3 - playwright/tests/data-extractor.spec.ts | 8 +- playwright/tests/data-viewer.spec.ts | 7 +- playwright/tests/limits-monitor.spec.ts | 7 +- 11 files changed, 703 insertions(+), 201 deletions(-) diff --git a/openc3/lib/openc3/api/config_api.rb b/openc3/lib/openc3/api/config_api.rb index e7e849886b..8d73f7f4e9 100644 --- a/openc3/lib/openc3/api/config_api.rb +++ b/openc3/lib/openc3/api/config_api.rb @@ -14,10 +14,10 @@ # GNU Affero General Public License for more details. # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved # -# This file may also be used under the terms of a commercial license +# This file may also be used under the terms of a commercial license # if purchased from OpenC3, Inc. require 'openc3/models/tool_config_model' @@ -26,18 +26,12 @@ module OpenC3 module Api WHITELIST ||= [] WHITELIST.concat([ - 'get_saved_config', 'list_configs', 'load_config', 'save_config', 'delete_config' ]) - # Get a saved configuration zip file - def get_saved_config(configuration_name = nil, scope: $openc3_scope, token: $openc3_token) - raise "Not supported by OpenC3 5" - end - def list_configs(tool, scope: $openc3_scope, token: $openc3_token) authorize(permission: 'system', scope: scope, token: token) ToolConfigModel.list_configs(tool, scope: scope) diff --git a/openc3/python/openc3/script/api_shared.py b/openc3/python/openc3/script/api_shared.py index 5b824a0717..1b12edd451 100644 --- a/openc3/python/openc3/script/api_shared.py +++ b/openc3/python/openc3/script/api_shared.py @@ -85,8 +85,9 @@ def check_exception(method_name, *args, **kwargs): Raises a CheckError if an Exception is not raised. Usage: check_exception(method_name, method_params}""" try: - orig_kwargs = kwargs.clone - if not kwargs["scope"]: + method = method_name + orig_kwargs = kwargs.copy() + if "scope" not in kwargs: kwargs["scope"] = OPENC3_SCOPE getattr(sys.modules[__name__], method_name)(*args, **kwargs) method = f"{method_name}({', '.join(args)}" @@ -131,14 +132,10 @@ def check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): for i in range(len(value)): range_bottom = expected_value[i] - tolerance[i] range_top = expected_value[i] + tolerance[i] - check_str = "CHECK: {:s}[{:d}]".format( - _upcase(target_name, packet_name, item_name), i - ) - range_str = "range {:g} to {:g} with value == {:g}".format( - range_bottom, range_top, value[i] - ) + check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}[{i}]" + range_str = f"range {round(range_bottom, 2)} to {round(range_top, 2)} with value == {value[i]}" if value[i] >= range_bottom and value[i] <= range_top: - message += f"{check_str} was within #{range_str}\n" + message += f"{check_str} was within {range_str}\n" else: message += f"{check_str} failed to be within {range_str}\n" all_checks_ok = False @@ -153,10 +150,8 @@ def check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): else: range_bottom = expected_value - tolerance range_top = expected_value + tolerance - check_str = "CHECK: {:s}".format(_upcase(target_name, packet_name, item_name)) - range_str = "range {:g} to {:g} with value == {:g}".format( - range_bottom, range_top, value - ) + check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}" + range_str = f"range {round(range_bottom, 2)} to {round(range_top, 2)} with value == {value}" if value >= range_bottom and value <= range_top: Logger.info(f"{check_str} was within {range_str}") else: @@ -170,7 +165,7 @@ def check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): def check_expression(exp_to_eval, locals=None): """Check to see if an expression is true without waiting. If the expression is not true, the script will pause.""" - success = _openc3_script_wait_implementation_expression( + success = _openc3_script_wait_expression( exp_to_eval, 0, DEFAULT_TLM_POLLING_RATE, locals ) if success: @@ -297,7 +292,7 @@ def wait_tolerance(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): expected_value, tolerance = _array_tolerance_process_args( len(value), expected_value, tolerance, "wait_tolerance" ) - success, value = _openc3_script_wait_implementation_array_tolerance( + success, value = _openc3_script_wait_array_tolerance( len(value), target_name, packet_name, @@ -314,14 +309,10 @@ def wait_tolerance(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): for i in range(0, len(value)): range_bottom = expected_value[i] - tolerance[i] range_top = expected_value[i] + tolerance[i] - check_str = "WAIT: {:s}[{:d}]".format( - _upcase(target_name, packet_name, item_name), i - ) - range_str = "range {:g} to {:g} with value == {:g} after waiting {:g} seconds".format( - range_bottom, range_top, value[i], time_diff - ) + check_str = f"WAIT: {_upcase(target_name, packet_name, item_name)}[{i}]" + range_str = f"range {range_bottom} to {range_top} with value == {value[i]} after waiting {time_diff} seconds" if value[i] >= range_bottom and value[i] <= range_top: - message += f"{check_str} was within #{range_str}\n" + message += f"{check_str} was within {range_str}\n" else: message += f"{check_str} failed to be within {range_str}\n" @@ -331,7 +322,7 @@ def wait_tolerance(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): else: Logger.warn(message) else: - success, value = _openc3_script_wait_implementation_tolerance( + success, value = _openc3_script_wait_tolerance( target_name, packet_name, item_name, @@ -344,12 +335,8 @@ def wait_tolerance(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): time_diff = time.time() - start_time range_bottom = expected_value - tolerance range_top = expected_value + tolerance - wait_str = "WAIT: {:s}".format(_upcase(target_name, packet_name, item_name)) - range_str = ( - "range {:g} to {:g} with value == {:g} after waiting {:g} seconds".format( - range_bottom, range_top, value, time_diff - ) - ) + wait_str = f"WAIT: {_upcase(target_name, packet_name, item_name)}" + range_str = f"range {range_bottom} to {range_top} with value == {value} after waiting {time_diff} seconds" if not quiet: if success: Logger.info(f"{wait_str} was within {range_str}") @@ -367,9 +354,7 @@ def wait_expression( ): """Wait on a custom expression to be true""" start_time = time.time() - success = _openc3_script_wait_implementation_expression( - exp_to_eval, timeout, polling_rate, locals - ) + success = _openc3_script_wait_expression(exp_to_eval, timeout, polling_rate, locals) time_diff = time.time() - start_time if not quiet: if success: @@ -399,7 +384,7 @@ def wait_check(*args, type="CONVERTED", scope=OPENC3_SCOPE): polling_rate, ) = _wait_check_process_args(args) start_time = time.time() - success, value = _openc3_script_wait_implementation( + success, value = _openc3_script_wait_value( target_name, packet_name, item_name, @@ -409,9 +394,9 @@ def wait_check(*args, type="CONVERTED", scope=OPENC3_SCOPE): polling_rate, ) time_diff = time.time() - start_time - check_str = "CHECK: {:s} {:s}".format( - _upcase(target_name, packet_name, item_name), comparison_to_eval - ) + check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}" + if comparison_to_eval: + check_str += f" {comparison_to_eval}" with_value_str = f"with value == {str(value)} after waiting {time_diff} seconds" if success: Logger.info(f"{check_str} success {with_value_str}") @@ -452,7 +437,7 @@ def wait_check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): expected_value, tolerance = _array_tolerance_process_args( len(value), expected_value, tolerance, "wait_check_tolerance" ) - success, value = _openc3_script_wait_implementation_array_tolerance( + success, value = _openc3_script_wait_array_tolerance( len(value), target_name, packet_name, @@ -469,14 +454,10 @@ def wait_check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): for i in range(0, len(value)): range_bottom = expected_value[i] - tolerance[i] range_top = expected_value[i] + tolerance[i] - check_str = "WAIT: {:s}[{:d}]".format( - _upcase(target_name, packet_name, item_name), i - ) - range_str = "range {:g} to {:g} with value == {:g} after waiting {:g} seconds".format( - range_bottom, range_top, value[i], time_diff - ) + check_str = f"WAIT: {_upcase(target_name, packet_name, item_name)}[{i}]" + range_str = f"range {range_bottom} to {range_top} with value == {value[i]} after waiting {time_diff} seconds" if value[i] >= range_bottom and value[i] <= range_top: - message += f"{check_str} was within #{range_str}\n" + message += f"{check_str} was within {range_str}\n" else: message += f"{check_str} failed to be within {range_str}\n" @@ -488,7 +469,7 @@ def wait_check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): else: raise CheckError(message) else: - success, value = _openc3_script_wait_implementation_tolerance( + success, value = _openc3_script_wait_tolerance( target_name, packet_name, item_name, @@ -502,12 +483,8 @@ def wait_check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): time_diff = time.time() - start_time range_bottom = expected_value - tolerance range_top = expected_value + tolerance - check_str = "CHECK: {:s}".format(_upcase(target_name, packet_name, item_name)) - range_str = ( - "range {:g} to {:g} with value == {:g} after waiting {:g} seconds".format( - range_bottom, range_top, value, time_diff - ) - ) + check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}" + range_str = f"range {range_bottom} to {range_top} with value == {value} after waiting {time_diff} seconds" if success: Logger.info(f"{check_str} was within {range_str}") else: @@ -524,7 +501,7 @@ def wait_check_expression( ): """Wait on an expression to be true. On a timeout, the script will pause""" start_time = time.time() - success = _openc3_script_wait_implementation_expression( + success = _openc3_script_wait_expression( exp_to_eval, timeout, polling_rate, context ) time_diff = time.time() - start_time @@ -618,7 +595,7 @@ def get_max_output(): # begin # Kernel::load(procedure_name) # rescue LoadError => error - # raise LoadError, "Error loading -- #{procedure_name}\n#{error.message}" + # raise LoadError, f"Error loading -- {procedure_name}\n{error.message}" # end # # Return whether we had to load and instrument this file, i.e. it was not cached # !cached @@ -655,9 +632,7 @@ def get_max_output(): def _upcase(target_name, packet_name, item_name): """Creates a string with the parameters upcased""" - return "{:s} {:s} {:s}".format( - target_name.upper(), packet_name.upper(), item_name.upper() - ) + return f"{target_name.upper()} {packet_name.upper()} {item_name.upper()}" def _check(*args, type="CONVERTED", scope=OPENC3_SCOPE): @@ -675,9 +650,7 @@ def _check(*args, type="CONVERTED", scope=OPENC3_SCOPE): target_name, packet_name, item_name, comparison_to_eval, value ) else: - Logger.info( - "CHECK: %s == %s", _upcase(target_name, packet_name, item_name), str(value) - ) + Logger.info(f"CHECK: {_upcase(target_name, packet_name, item_name)} == {value}") def _check_process_args(args, method_name): @@ -689,6 +662,11 @@ def _check_process_args(args, method_name): item_name, comparison_to_eval, ) = extract_fields_from_check_text(args[0]) + case 3: + target_name = args[0] + packet_name = args[1] + item_name = args[2] + comparison_to_eval = None case 4: target_name = args[0] packet_name = args[1] @@ -699,9 +677,9 @@ def _check_process_args(args, method_name): raise RuntimeError( f"ERROR: Invalid number of arguments ({len(args)}) passed to {method_name}()" ) - if not comparison_to_eval.isascii(): + if comparison_to_eval and not comparison_to_eval.isascii(): raise RuntimeError( - f"Invalid comparison to non-ascii value: {comparison_to_eval}" + f"ERROR: Invalid comparison to non-ascii value: {comparison_to_eval}" ) return target_name, packet_name, item_name, comparison_to_eval @@ -754,7 +732,7 @@ def _wait_packet( if not initial_count: initial_count = 0 start_time = time.time() - success, value = _openc3_script_wait_implementation( + success, value = _openc3_script_wait_value( target_name, packet_name, "RECEIVED_COUNT", @@ -771,23 +749,10 @@ def _wait_packet( if success: if not quiet: Logger.info( - "{:s}: {:s} {:s} received {:d} times after waiting {:g} seconds".format( - type, - target_name.upper(), - packet_name.upper(), - value - initial_count, - time_diff, - ) + f"{type}: {target_name.upper()} {packet_name.upper()} received {value - initial_count} times after waiting {time_diff} seconds" ) else: - message = "{:s}: {:s} {:s} expected to be received {:d} times but only received {:d} times after waiting {:g} seconds".format( - type, - target_name.upper(), - packet_name.upper(), - num_packets, - value - initial_count, - time_diff, - ) + message = f"{type}: {target_name.upper()} {packet_name.upper()} expected to be received {num_packets} times but only received {value - initial_count} times after waiting {time_diff} seconds" if check: if openc3.script.DISCONNECT: Logger.error(message) @@ -810,7 +775,7 @@ def _execute_wait( scope, ): start_time = time.time() - success, value = _openc3_script_wait_implementation( + success, value = _openc3_script_wait_value( target_name, packet_name, item_name, @@ -823,8 +788,8 @@ def _execute_wait( if type(value) == str: value = f"'{value}'" # Show user the check against a quoted string time_diff = time.time() - start_time - wait_str = "WAIT: {:s} {:s}".format( - _upcase(target_name, packet_name, item_name), comparison_to_eval + wait_str = ( + f"WAIT: {_upcase(target_name, packet_name, item_name)} {comparison_to_eval}" ) value_str = f"with value == {value} after waiting {time_diff} seconds" if not quiet: @@ -865,9 +830,7 @@ def _wait_tolerance_process_args(args, function_name): else: # Invalid number of arguments raise RuntimeError( - "ERROR: Invalid number of arguments ({:d}) passed to {:s}()".format( - length, function_name - ) + f"ERROR: Invalid number of arguments ({length}) passed to {function_name}()" ) return ( target_name, @@ -942,7 +905,7 @@ def _wait_check_process_args(args): ) -def _openc3_script_wait_implementation( +def _openc3_script_wait( target_name, packet_name, item_name, @@ -954,8 +917,8 @@ def _openc3_script_wait_implementation( ): value = None end_time = time.time() + timeout - if not exp_to_eval.isascii(): - raise RuntimeError("Invalid comparison to non-ascii value") + if exp_to_eval and not exp_to_eval.isascii(): + raise RuntimeError("ERROR: Invalid comparison to non-ascii value") try: while True: @@ -997,7 +960,7 @@ def _openc3_script_wait_implementation( # Wait for a converted telemetry item to pass a comparison -def _openc3_script_wait_implementation( +def _openc3_script_wait_value( target_name, packet_name, item_name, @@ -1011,7 +974,7 @@ def _openc3_script_wait_implementation( exp_to_eval = "value " + comparison_to_eval else: exp_to_eval = None - return _openc3_script_wait_implementation( + return _openc3_script_wait( target_name, packet_name, item_name, @@ -1023,7 +986,7 @@ def _openc3_script_wait_implementation( ) -def _openc3_script_wait_implementation_tolerance( +def _openc3_script_wait_tolerance( target_name, packet_name, item_name, @@ -1034,10 +997,8 @@ def _openc3_script_wait_implementation_tolerance( polling_rate=DEFAULT_TLM_POLLING_RATE, scope=OPENC3_SCOPE, ): - exp_to_eval = "(value >= ({:g} - {:g}) and value <= ({:g} + {:g}))".format( - expected_value, abs(tolerance), expected_value, abs(tolerance) - ) - return _openc3_script_wait_implementation( + exp_to_eval = f"(value >= ({expected_value} - {abs(tolerance)}) and value <= ({expected_value} + {abs(tolerance)}))" + return _openc3_script_wait( target_name, packet_name, item_name, @@ -1049,7 +1010,7 @@ def _openc3_script_wait_implementation_tolerance( ) -def _openc3_script_wait_implementation_array_tolerance( +def _openc3_script_wait_array_tolerance( array_size, target_name, packet_name, @@ -1064,15 +1025,10 @@ def _openc3_script_wait_implementation_array_tolerance( statements = [] for i in range(array_size): statements.append( - "(value >= ({:g} - {:g}) and value <= ({:g} + {:g}))".format( - expected_value[i], - abs(tolerance[i]), - expected_value[i], - abs(tolerance[i]), - ) + f"(value >= ({expected_value[i]} - {abs(tolerance[i])}) and value <= ({expected_value[i]} + {abs(tolerance[i])})" ) exp_to_eval = " and ".join(statements) - return _openc3_script_wait_implementation( + return _openc3_script_wait( target_name, packet_name, item_name, @@ -1084,9 +1040,7 @@ def _openc3_script_wait_implementation_array_tolerance( ) -def _openc3_script_wait_implementation_expression( - exp_to_eval, timeout, polling_rate, locals=None -): +def _openc3_script_wait_expression(exp_to_eval, timeout, polling_rate, locals=None): """Wait on an expression to be true.""" end_time = time.time() + timeout if not exp_to_eval.isascii(): @@ -1126,8 +1080,8 @@ def _openc3_script_wait_implementation_expression( def _check_eval(target_name, packet_name, item_name, comparison_to_eval, value): string = "value " + comparison_to_eval - check_str = "CHECK: {:s} {:s}".format( - _upcase(target_name, packet_name, item_name), comparison_to_eval + check_str = ( + f"CHECK: {_upcase(target_name, packet_name, item_name)} {comparison_to_eval}" ) # Show user the check against a quoted string # Note: We have to preserve the original 'value' variable because we're going to eval against it diff --git a/openc3/python/ruby_to_python.py b/openc3/python/ruby_to_python.py index 37557b83fe..314ebc3ec5 100644 --- a/openc3/python/ruby_to_python.py +++ b/openc3/python/ruby_to_python.py @@ -140,6 +140,8 @@ m = re.compile(r"(\s*)expect\((.*)\)\.to include\((.*)\)").match(line) if m: line = f"{m.group(1)}self.assertIn([{m.group(3)}], {m.group(2)})\n" + line = line.replace("capture_io do |stdout|", "for stdout in capture_io():") + line = line.replace("stdout.string", "stdout.getvalue()") # Ruby: target_names.each do |target_name| # Python: for target_name in target_names: diff --git a/openc3/python/test/interfaces/test_simulated_target_interface.py b/openc3/python/test/interfaces/test_simulated_target_interface.py index 1780372772..9b32b2a914 100644 --- a/openc3/python/test/interfaces/test_simulated_target_interface.py +++ b/openc3/python/test/interfaces/test_simulated_target_interface.py @@ -14,7 +14,6 @@ # This file may also be used under the terms of a commercial license # if purchased from OpenC3, Inc. -import os import unittest from unittest.mock import * from test.test_helper import * diff --git a/openc3/python/test/script/test_api_shared.py b/openc3/python/test/script/test_api_shared.py index eb780815eb..92364f9155 100644 --- a/openc3/python/test/script/test_api_shared.py +++ b/openc3/python/test/script/test_api_shared.py @@ -15,53 +15,638 @@ # if purchased from OpenC3, Inc. import unittest -from unittest.mock import patch +from unittest.mock import * from test.test_helper import * -import fakeredis from openc3.script.api_shared import * +from openc3.models.target_model import TargetModel -@patch("redis.Redis", return_value=fakeredis.FakeStrictRedis(version=7)) +class Proxy: + def tlm(target_name, packet_name, item_name, type, scope): + if scope != "DEFAULT": + raise RuntimeError(f"Packet '{target_name} {packet_name}' does not exist") + match item_name: + case "TEMP1": + match type: + case "RAW": + return 1 + case "CONVERTED": + return 10 + case "FORMATTED": + return "10.000" + case "WITH_UNITS": + return "10.000 C" + case "TEMP2": + match type: + case "RAW": + return 1.5 + case "CONVERTED": + return 10.5 + case "CCSDSSHF": + return "FALSE" + case "BLOCKTEST": + return b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + case "ARY": + return [2, 3, 4] + case _: + return None + + +@patch("openc3.script.API_SERVER", Proxy) class TestApiShared(unittest.TestCase): - pass - # @patch("openc3.script.telemetry.tlm_raw") - # @patch("openc3.script.telemetry.tlm") - # def test_check_telemetry_item_against_value(self, tlm, tlm_raw, Redis): - # for stdout in capture_io(): - # tlm.return_value = 10 - # check("INST", "HEALTH_STATUS", "TEMP1", "> 1") - # self.assertRegex( - # stdout.getvalue(), - # r"CHECK: INST HEALTH_STATUS TEMP1 > 1 success with value == 10", - # ) - - # tlm_raw.return_value = 1 - # check("INST HEALTH_STATUS TEMP1 == 1", type="RAW") - # self.assertRegex( - # stdout.getvalue(), - # r"CHECK: INST HEALTH_STATUS TEMP1 == 1 success with value == 1", - # ) - - # self.assertRaisesRegex( - # CheckError, - # r"CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10", - # check, - # "INST HEALTH_STATUS TEMP1 > 100", - # ) - - # @patch("openc3.script.telemetry.tlm") - # def test_check_warns_when_checking_a_state_against_a_constant(self, tlm, Redis): - # tlm.return_value = "FALSE" - # for stdout in capture_io(): - # check("INST HEALTH_STATUS CCSDSSHF == 'FALSE'") - # self.assertRegex( - # stdout.getvalue(), - # r"CHECK: INST HEALTH_STATUS CCSDSSHF == 'FALSE' success with value == 'FALSE'", - # ) - - # self.assertRaisesRegex( - # NameError, - # r"Uninitialized constant FALSE. Did you mean 'FALSE' as a string", - # check, - # "INST HEALTH_STATUS CCSDSSHF == FALSE", - # ) + def setUp(self): + mock_redis(self) + setup_system() + # case 'RECEIVED_COUNT': + # if self.count: + # received_count += 1 + # received_count + # else: + # None + + model = TargetModel(folder_name="INST", name="INST", scope="DEFAULT") + model.create() + + def test_check_raises_with_invalid_params(self): + with self.assertRaisesRegex( + RuntimeError, r"ERROR: Invalid number of arguments \(2\) passed to check" + ): + check("INST", "HEALTH_STATUS") + + def test_check_raises_when_checking_against_binary(self): + with self.assertRaisesRegex( + RuntimeError, "ERROR: Invalid comparison to non-ascii value" + ): + check("INST HEALTH_STATUS TEMP1 == \xFF") + + def test_check_prints_the_value_with_no_comparision(self): + for stdout in capture_io(): + check("INST", "HEALTH_STATUS", "TEMP1") + self.assertIn("CHECK: INST HEALTH_STATUS TEMP1 == 10", stdout.getvalue()) + check("INST HEALTH_STATUS TEMP1", type="RAW") + self.assertIn("CHECK: INST HEALTH_STATUS TEMP1 == 1", stdout.getvalue()) + + def test_check_checks_a_telemetry_item_against_a_value(self): + for stdout in capture_io(): + check("INST", "HEALTH_STATUS", "TEMP1", "> 1") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 > 1 success with value == 10", + stdout.getvalue(), + ) + check("INST HEALTH_STATUS TEMP1 == 1", type="RAW") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 == 1 success with value == 1", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10", + ): + check("INST HEALTH_STATUS TEMP1 > 100") + + def test_check_logs_instead_of_raises_when_disconnected(self): + openc3.script.DISCONNECT = True + for stdout in capture_io(): + check("INST HEALTH_STATUS TEMP1 > 100") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10", + stdout.getvalue(), + ) + openc3.script.DISCONNECT = False + + def test_check_warns_when_checking_a_state_against_a_constant(self): + for stdout in capture_io(): + check("INST HEALTH_STATUS CCSDSSHF == 'FALSE'") + self.assertIn( + "CHECK: INST HEALTH_STATUS CCSDSSHF == 'FALSE' success with value == 'FALSE'", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + NameError, + "Uninitialized constant FALSE. Did you mean 'FALSE' as a string?", + ): + check("INST HEALTH_STATUS CCSDSSHF == FALSE") + + def test_checks_against_the_specified_type(self): + for stdout in capture_io(): + check_raw("INST HEALTH_STATUS TEMP1 == 1") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 == 1 success", stdout.getvalue() + ) + check_formatted("INST HEALTH_STATUS TEMP1 == '10.000'") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 == '10.000' success", stdout.getvalue() + ) + check_with_units("INST HEALTH_STATUS TEMP1 == '10.000 C'") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 == '10.000 C' success", + stdout.getvalue(), + ) + + def test_checks_that_the_exception_is_raised_in_our_apis(self): + for stdout in capture_io(): + check_exception( + "check", "INST HEALTH_STATUS TEMP1 == 9", type="RAW", scope="DEFAULT" + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 == 9 failed", stdout.getvalue() + ) + check_exception( + "check", "INST HEALTH_STATUS TEMP1 == 9", type="RAW", scope="OTHER" + ) + self.assertIn( + "Packet 'INST HEALTH_STATUS' does not exist", stdout.getvalue() + ) + + def test_raises_if_the_exception_is_not_raised(self): + with self.assertRaisesRegex( + CheckError, + r"check\(INST HEALTH_STATUS TEMP1 == 10\) should have raised an exception but did not", + ): + check_exception("check", "INST HEALTH_STATUS TEMP1 == 10") + + def test_check_tolerance_raises_with_invalid_params(self): + with self.assertRaisesRegex( + RuntimeError, + r"ERROR: Invalid number of arguments \(4\) passed to check_tolerance", + ): + check_tolerance("INST", "HEALTH_STATUS", 1.55, 0.1, type="RAW") + + def test_check_tolerance_raises_with_formatted_or_with_units(self): + with self.assertRaisesRegex( + RuntimeError, r"Invalid type 'FORMATTED' for check_tolerance" + ): + check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type="FORMATTED") + with self.assertRaisesRegex( + RuntimeError, r"Invalid type 'WITH_UNITS' for check_tolerance" + ): + check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type="WITH_UNITS") + + def test_checks_that_a_value_is_within_a_tolerance(self): + for stdout in capture_io(): + check_tolerance("INST", "HEALTH_STATUS", "TEMP2", 1.55, 0.1, type="RAW") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65 with value == 1.5", + stdout.getvalue(), + ) + check_tolerance("INST HEALTH_STATUS TEMP2", 10.5, 0.01) + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP2 was within range 10.49 to 10.51 with value == 10.5", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + f"CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5", + ): + check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1) + + def test_check_tolerance_logs_instead_of_raises_exception_when_disconnected(self): + openc3.script.DISCONNECT = True + for stdout in capture_io(): + check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1) + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5", + stdout.getvalue(), + ) + for stdout in capture_io(): + check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1) + self.assertIn( + "INST HEALTH_STATUS ARY[0] failed to be within range 2.9 to 3.1 with value == 2", + stdout.getvalue(), + ) + openc3.script.DISCONNECT = False + + def test_checks_that_an_array_value_is_within_a_single_tolerance(self): + for stdout in capture_io(): + check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[1] was within range 2 to 4 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[2] was within range 2 to 4 with value == 4", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2", + ): + check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS ARY\[1\] was within range 2.9 to 3.1 with value == 3", + ): + check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS ARY\[2\] failed to be within range 2.9 to 3.1 with value == 4", + ): + check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1) + + def test_checks_that_multiple_array_values_are_within_tolerance(self): + for stdout in capture_io(): + check_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + r"INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2", + ): + check_tolerance("INST HEALTH_STATUS ARY", [3, 3, 4], 0.1) + with self.assertRaisesRegex( + RuntimeError, r"ERROR: Invalid array size for expected_value" + ): + check_tolerance("INST HEALTH_STATUS ARY", [1, 2, 3, 4], 0.1) + + def test_checks_that_an_array_value_is_within_multiple_tolerances(self): + for stdout in capture_io(): + check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, [1, 0.1, 2]) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + r"INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2", + ): + check_tolerance("INST HEALTH_STATUS ARY", 3, [0.1, 0.1, 2]) + with self.assertRaisesRegex( + RuntimeError, r"ERROR: Invalid array size for tolerance" + ): + check_tolerance("INST HEALTH_STATUS ARY", 3, [0.1, 0.1, 2, 3]) + + +# class CheckToleranceRaw(unittest.TestCase): +# def test_checks_that_a_value_is_within_a_tolerance(self): +# for stdout in capture_io(): +# check_tolerance_raw("INST HEALTH_STATUS TEMP2", 1.55, 0.1) +# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65\d+ with value == 1.5', stdout.getvalue()) + +# class CheckExpression(unittest.TestCase): +# def test_checks_that_an_expression_is_True(self): +# for stdout in capture_io(): +# check_expression("True == True") +# self.assertIn('CHECK: True == True is TRUE', stdout.getvalue()) +# { check_expression("True == False") }.to raise_error(/CHECK: True == False is FALSE/) + +# def test_logs_instead_of_raises_when_disconnected(self): +# openc3.script.DISCONNECT = True +# for stdout in capture_io(): +# check_expression("True == False") +# self.assertIn('CHECK: True == False is FALSE', stdout.getvalue()) +# openc3.script.DISCONNECT = False + +# def test_checks_a_logical_expression(self): +# for stdout in capture_io(): +# check_expression("'STRING' == 'STRING'") +# self.assertIn('CHECK: 'STRING' == 'STRING' is TRUE', stdout.getvalue()) +# { check_expression("1 == 2") }.to raise_error(/CHECK: 1 == 2 is FALSE/) +# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant STRING. Did you mean 'STRING' as a string?"): +# check_expression("'STRING' == STRING") + +# class Wait(unittest.TestCase): +# def test_waits_for_an_indefinite_time(self): +# for stdout in capture_io(): +# wait() +# self.assertIn('WAIT: Indefinite for actual time of .* seconds', stdout.getvalue()) + +# def test_waits_for_a_relative_time(self): +# for stdout in capture_io(): +# wait(5) +# self.assertIn('WAIT: 5 seconds with actual time of .* seconds', stdout.getvalue()) + +# def test_raises_on_a_non_numeric_time(self): +# { wait('5') }.to raise_error("Non-numeric wait time specified") + +# def test_waits_for_a_tgt_pkt_item(self): +# for stdout in capture_io(): +# wait("INST HEALTH_STATUS TEMP1 > 0", 5) +# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 > 0 success with value == 10 after waiting .* seconds', stdout.getvalue()) + +# wait("INST HEALTH_STATUS TEMP1 < 0", 0.1, 0.1) # Last param is polling rate +# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 < 0 failed with value == 10 after waiting .* seconds', stdout.getvalue()) + +# wait("INST", "HEALTH_STATUS", "TEMP1", "> 0", 5) +# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 > 0 success with value == 10 after waiting .* seconds', stdout.getvalue()) + +# wait("INST", "HEALTH_STATUS", "TEMP1", "== 0", 0.1, 0.1) # Last param is polling rate +# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 == 0 failed with value == 10 after waiting .* seconds', stdout.getvalue()) + +# { wait("INST", "HEALTH_STATUS", "TEMP1", "== 0", 0.1, 0.1, 0.1) }.to raise_error(/Invalid number of arguments/) + + +# class WaitTolerance(unittest.TestCase): +# def test_raises_with_invalid_params(self): +# { wait_tolerance("INST", "HEALTH_STATUS", "TEMP2", type= 'RAW') }.to raise_error(/ERROR: Invalid number of arguments \(3\) passed to wait_tolerance/) +# { wait_tolerance("INST", "HEALTH_STATUS", 1.55, 0.1, type= 'RAW') }.to raise_error(/ERROR: Telemetry Item must be specified/) + +# def test_raises_with_=formatted_or_=with_units(self): +# { wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type= 'FORMATTED') }.to raise_error("Invalid type 'FORMATTED' for wait_tolerance") +# { wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type= 'WITH_UNITS') }.to raise_error("Invalid type 'WITH_UNITS' for wait_tolerance") + +# def test_waits_for_a_value_to_be_within_a_tolerance(self): +# for stdout in capture_io(): +# wait_tolerance("INST", "HEALTH_STATUS", "TEMP2", 1.55, 0.1, 5, type= 'RAW') +# self.assertIn('WAIT: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65\d+ with value == 1.5', stdout.getvalue()) +# wait_tolerance("INST HEALTH_STATUS TEMP2", 10.5, 0.01, 5) +# self.assertIn(["WAIT: INST HEALTH_STATUS TEMP2 was within range 10.49 to 10.51 with value == 10.5"], stdout.getvalue()) +# wait_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) +# self.assertIn(["WAIT: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5"], stdout.getvalue()) + +# def test_checks_that_an_array_value_is_within_a_single_tolerance(self): +# for stdout in capture_io(): +# wait_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1, 5) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2 to 4 with value == 3"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 2 to 4 with value == 4"], stdout.getvalue()) +# wait_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) +# self.assertIn(["INST HEALTH_STATUS ARY[0] failed to be within range 2.9 to 3.1 with value == 2"], stdout.getvalue()) +# self.assertIn(["INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) +# self.assertIn(["INST HEALTH_STATUS ARY[2] failed to be within range 2.9 to 3.1 with value == 4"], stdout.getvalue()) + +# def test_checks_that_multiple_array_values_are_within_tolerance(self): +# for stdout in capture_io(): +# wait_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1, 5) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4"], stdout.getvalue()) + +# wait_tolerance("INST HEALTH_STATUS ARY", [2, 3, 4], 0.1, 5) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4"], stdout.getvalue()) + +# def test_checks_that_an_array_value_is_within_multiple_tolerances(self): +# for stdout in capture_io(): +# wait_tolerance("INST", "HEALTH_STATUS", "ARY", 3, [1, 0.1, 2], 5) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4"], stdout.getvalue()) + +# wait_tolerance("INST HEALTH_STATUS ARY", 3, [1, 0.1, 2], 5) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) +# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4"], stdout.getvalue()) + +# class WaitExpression(unittest.TestCase): +# [True, False].each do |cancel| +# context "with wait cancelled {cancel}" do +# def test_waits_for_an_expression(self): +# self.sleep_cancel = cancel +# for stdout in capture_io(): +# wait_expression("True == True", 5) +# self.assertIn('WAIT: True == True is TRUE after waiting .* seconds', stdout.getvalue()) +# wait_expression("True == False", 0.1) +# self.assertIn('WAIT: True == False is FALSE after waiting .* seconds', stdout.getvalue()) + +# def test_checks_a_logical_expression(self): +# for stdout in capture_io(): +# wait_expression("'STRING' == 'STRING'", 5) +# self.assertIn('WAIT: 'STRING' == 'STRING' is TRUE after waiting .* seconds', stdout.getvalue()) +# wait_expression("1 == 2", 0.1) +# self.assertIn('WAIT: 1 == 2 is FALSE after waiting .* seconds', stdout.getvalue()) +# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant STRING. Did you mean 'STRING' as a string?"): +# wait_expression("'STRING' == STRING", 5) + +# class WaitCheck(unittest.TestCase): +# def test_raises_with_invalid_params(self): +# { wait_check("INST HEALTH_STATUS TEMP1") }.to raise_error(/ERROR: Invalid number of arguments \(1\) passed to wait_check/) + +# def test_checks_a_telemetry_item_against_a_value(self): +# for stdout in capture_io(): +# wait_check("INST", "HEALTH_STATUS", "TEMP1", "> 1", 0.01, 0.1) # Last param is polling rate +# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 > 1 success with value == 10'], stdout.getvalue()) +# wait_check("INST HEALTH_STATUS TEMP1 == 1", 0.01, 0.1, type= 'RAW') +# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 == 1 success with value == 1'], stdout.getvalue()) +# { wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) }.to raise_error(/CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10/) + +# [True, False].each do |cancel| +# context "with wait cancelled {cancel}" do +# def test_handles_a_block(self): +# self.sleep_cancel = cancel + +# for stdout in capture_io(): +# wait_check("INST HEALTH_STATUS TEMP1", 0.01) do |value| +# value == 10 +# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 success with value == 10'], stdout.getvalue()) + +# { +# wait_check("INST HEALTH_STATUS TEMP1", 0.01) do |value| +# value == 1 +# }.to raise_error(/CHECK: INST HEALTH_STATUS TEMP1 failed with value == 10/) + +# def test_logs_instead_of_raises_when_disconnected(self): +# openc3.script.DISCONNECT = True +# for stdout in capture_io(): +# wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) +# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10'], stdout.getvalue()) +# openc3.script.DISCONNECT = False + +# def test_fails_against_binary_data(self): +# data = "\xFF" * 10 +# with self.assertRaisesRegex(AttributeError, f"ERROR: Invalid comparison to non-ascii value"): +# wait_check("INST HEALTH_STATUS BLOCKTEST == '{data}'", 0.01) + +# def test_warns_when_checking_a_state_against_a_constant(self): +# for stdout in capture_io(): +# wait_check("INST HEALTH_STATUS CCSDSSHF == 'FALSE'", 0.01) +# self.assertIn(["CHECK: INST HEALTH_STATUS CCSDSSHF == 'FALSE' success with value == 'FALSE'"], stdout.getvalue()) +# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant FALSE. Did you mean 'FALSE' as a string?"): +# wait_check("INST HEALTH_STATUS CCSDSSHF == FALSE", 0.01) + +# class WaitCheckTolerance(unittest.TestCase): +# def test_raises_with_=formatted_or_=with_units(self): +# { wait_check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, 5, type= 'FORMATTED') }.to raise_error("Invalid type 'FORMATTED' for wait_check_tolerance") +# { wait_check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, 5, type= 'WITH_UNITS') }.to raise_error("Invalid type 'WITH_UNITS' for wait_check_tolerance") + +# def test_checks_that_a_value_is_within_a_tolerance(self): +# for stdout in capture_io(): +# wait_check_tolerance("INST", "HEALTH_STATUS", "TEMP2", 1.55, 0.1, 5, type= 'RAW') +# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65\d+ with value == 1.5', stdout.getvalue()) +# wait_check_tolerance("INST HEALTH_STATUS TEMP2", 10.5, 0.01, 5) +# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 was within range 10.49 to 10.51 with value == 10.5', stdout.getvalue()) +# with self.assertRaisesRegex(AttributeError, f"CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5"): +# wait_check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) + +# def test_checks_that_an_array_value_is_within_a_single_tolerance(self): +# for stdout in capture_io(): +# wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1, 5) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[1] was within range 2 to 4 with value == 3"], stdout.getvalue()) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[2] was within range 2 to 4 with value == 4"], stdout.getvalue()) +# { wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2/) +# { wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[1\] was within range 2.9 to 3.1 with value == 3/) +# { wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[2\] failed to be within range 2.9 to 3.1 with value == 4/) + +# def test_logs_instead_of_raises_when_disconnected(self): +# openc3.script.DISCONNECT = True +# for stdout in capture_io(): +# wait_check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) +# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5', stdout.getvalue()) +# for stdout in capture_io(): +# wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) +# self.assertIn('CHECK: INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2', stdout.getvalue()) +# openc3.script.DISCONNECT = False + +# def test_checks_that_multiple_array_values_are_within_tolerance(self): +# for stdout in capture_io(): +# wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1, 5) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2"], stdout.getvalue()) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4"], stdout.getvalue()) + +# def test_checks_that_an_array_value_is_within_multiple_tolerances(self): +# for stdout in capture_io(): +# wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, [1, 0.1, 2], 5) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) +# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4"], stdout.getvalue()) + +# class WaitCheckExpression(unittest.TestCase): +# def test_waits_and_checks_that_an_expression_is_True(self): +# for stdout in capture_io(): +# wait_check_expression("True == True", 5) +# self.assertIn('CHECK: True == True is TRUE', stdout.getvalue()) +# { wait_check_expression("True == False", 0.1) }.to raise_error(/CHECK: True == False is FALSE/) + +# def test_logs_instead_of_raises_when_disconnected(self): +# openc3.script.DISCONNECT = True +# for stdout in capture_io(): +# wait_check_expression("True == False", 5) +# self.assertIn('CHECK: True == False is FALSE', stdout.getvalue()) +# openc3.script.DISCONNECT = False + +# def test_waits_and_checks_a_logical_expression(self): +# for stdout in capture_io(): +# wait_check_expression("'STRING' == 'STRING'", 5) +# self.assertIn('CHECK: 'STRING' == 'STRING' is TRUE', stdout.getvalue()) +# { wait_check_expression("1 == 2", 0.1) }.to raise_error(/CHECK: 1 == 2 is FALSE/) +# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant STRING. Did you mean 'STRING' as a string?"): +# wait_check_expression("'STRING' == STRING", 0.1) + +# [True, False].each do |cancel| +# context "with wait cancelled {cancel}" do + +# class WaitPacket(unittest.TestCase): +# def setUp(self): +# self.sleep_cancel = cancel + +# def test_prints_warning_if_packet_not_received(self): +# self.count = False +# for stdout in capture_io(): +# wait_packet("INST", "HEALTH_STATUS", 1, 0.5) +# self.assertIn('WAIT: INST HEALTH_STATUS expected to be received 1 times but only received 0 times', stdout.getvalue()) + +# def test_prints_success_if_the_packet_is_received(self): +# self.count = True +# for stdout in capture_io(): +# wait_packet("INST", "HEALTH_STATUS", 5, 0.5) +# if cancel: +# self.assertIn('WAIT: INST HEALTH_STATUS expected to be received 5 times', stdout.getvalue()) +# else: +# self.assertIn('WAIT: INST HEALTH_STATUS received 5 times after waiting', stdout.getvalue()) + +# class WaitCheckPacket(unittest.TestCase): +# def setUp(self): +# self.sleep_cancel = cancel + +# def test_raises_a_check_error_if_packet_not_received(self): +# self.count = False +# { wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) }.to raise_error(/CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times/) + +# def test_logs_instead_of_raises_if_disconnected(self): +# openc3.script.DISCONNECT = True +# self.count = False +# for stdout in capture_io(): +# wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) +# self.assertIn('CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times', stdout.getvalue()) +# openc3.script.DISCONNECT = False + +# def test_prints_success_if_the_packet_is_received(self): +# self.count = True +# for stdout in capture_io(): +# if cancel: +# { wait_check_packet("INST", "HEALTH_STATUS", 5, 0.5) }.to raise_error(/CHECK: INST HEALTH_STATUS expected to be received 5 times/) +# else: +# wait_check_packet("INST", "HEALTH_STATUS", 5, 0.5) +# self.assertIn('CHECK: INST HEALTH_STATUS received 5 times after waiting', stdout.getvalue()) + +# class DisableInstrumentation(unittest.TestCase): +# def test_does_nothing_if_runningscript_is_not_defined(self): +# for stdout in capture_io(): +# disable_instrumentation do +# puts "HI" +# expect(stdout.getvalue()).to match("HI") + +# def test_sets_runningscript.instance.use_instrumentation(self): +# class RunningScript: +# self.struct = OpenStruct() +# self.struct.use_instrumentation = True +# def self.instance: +# self.struct +# for stdout in capture_io(): +# self.assertTrue(RunningScript.instance.use_instrumentation) +# disable_instrumentation do +# self.assertFalse(RunningScript.instance.use_instrumentation) +# puts "HI" +# self.assertTrue(RunningScript.instance.use_instrumentation) +# expect(stdout.getvalue()).to match("HI") + +# class GetLineDelay, setLineDelay(unittest.TestCase): +# def test_gets_and_sets_runningscript.line_delay(self): +# class RunningScript: +# instance_attr_accessor :line_delay + +# set_line_delay(10) +# self.assertEqual(RunningScript.line_delay, 10) +# self.assertEqual(get_line_delay(), 10) + +# class GetMaxOutput, setMaxOutput(unittest.TestCase): +# def test_gets_and_sets_runningscript.max_output_characters(self): +# class RunningScript: +# instance_attr_accessor :max_output_characters + +# set_max_output(100) +# self.assertEqual(RunningScript.max_output_characters, 100) +# self.assertEqual(get_max_output(), 100) + +# class Start, loadUtility, requireUtility(unittest.TestCase): +# def test_loads_a_script(self): +# File.open("tester.rb", 'w') do |file| +# file.puts "# Nothing" + +# start("tester.rb") +# load_utility("tester.rb") +# result = require_utility("tester") +# self.assertTrue(result)() +# result = require_utility("tester") +# self.assertFalse(result) + +# File.delete('tester.rb') + +# { load_utility('tester.rb') }.to raise_error(LoadError) +# { start('tester.rb') }.to raise_error(LoadError) +# # Can't try tester.rb because it's already been loaded and cached +# { require_utility('does_not_exist.rb') }.to raise_error(LoadError) diff --git a/playwright/tests/admin/settings.spec.ts b/playwright/tests/admin/settings.spec.ts index 387cd78a58..244049c248 100644 --- a/playwright/tests/admin/settings.spec.ts +++ b/playwright/tests/admin/settings.spec.ts @@ -48,10 +48,7 @@ test('clears recent configs', async ({ page, utils }) => { await page.locator('text=Save Configuration').click() await page.locator('[data-test=name-input-save-config-dialog]').fill(config) await page.locator('button:has-text("Ok")').click() - await expect(page.getByText(`Saved configuration: ${config}`)).toBeVisible() - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} + await page.getByRole('button', { name: 'Dismiss' }).click() let localStorage = await page.evaluate(() => window.localStorage) expect(localStorage['lastconfig__data_viewer']).toBe(config) diff --git a/playwright/tests/autonomic.spec.ts b/playwright/tests/autonomic.spec.ts index faa502e06e..207830be3c 100644 --- a/playwright/tests/autonomic.spec.ts +++ b/playwright/tests/autonomic.spec.ts @@ -141,10 +141,7 @@ test('manually run a reaction', async ({ page, utils }) => { await page.locator('[data-test="clear-notifications"]').click() await utils.sleep(1000) // Give it a second to ensure clear await page.locator('[data-test="execute-actions"]').click() - await expect(page.getByText('Executed Reaction')).toBeVisible() - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} + await page.getByRole('button', { name: 'Dismiss' }).click() await expect(page.locator('[data-test="log-messages"]')).toContainText( 'REACT1 was executed', ) @@ -715,10 +712,7 @@ test('delete a reaction', async ({ page, utils }) => { ) await page.locator('[data-test="item-delete"]').nth(0).click() await page.locator('[data-test="confirm-dialog-delete"]').click() - await expect(page.getByText('Deleted Reaction')).toBeVisible() - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} + await page.getByRole('button', { name: 'Dismiss' }).click() await expect(page.locator('[data-test="log-messages"]')).toContainText( 'REACT1 was deleted', ) @@ -727,10 +721,7 @@ test('delete a reaction', async ({ page, utils }) => { await page.getByRole('tab', { name: 'Triggers' }).click() await page.locator('[data-test="item-delete"]').nth(0).click() await page.locator('[data-test="confirm-dialog-delete"]').click() - await expect(page.getByText('Deleted Trigger')).toBeVisible() - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} + await page.getByRole('button', { name: 'Dismiss' }).click() await expect(page.locator('[data-test="triggers-table"]')).not.toContainText( 'TRIG1', @@ -746,10 +737,7 @@ test('delete trigger group', async ({ page, utils }) => { await page.locator('[data-test="item-delete"]').nth(0).click() await page.locator('[data-test="confirm-dialog-delete"]').click() - await expect(page.getByText('Deleted Trigger')).toBeVisible() - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} + await page.getByRole('button', { name: 'Dismiss' }).click() await page.locator('[data-test="delete-group"]').click() await page.locator('[data-test="group-delete-submit-btn"]').click() diff --git a/playwright/tests/bucket-explorer.spec.ts b/playwright/tests/bucket-explorer.spec.ts index 819917d7df..c58244da09 100644 --- a/playwright/tests/bucket-explorer.spec.ts +++ b/playwright/tests/bucket-explorer.spec.ts @@ -121,9 +121,6 @@ test('direct URLs', async ({ page, utils }) => { await expect( page.getByText('Unknown bucket / volume OPENC3_BLAH_BUCKET'), ).toBeVisible() - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} // Prepending %2F makes it a volume await page.goto('/tools/bucketexplorer/%2FBAD') await expect( diff --git a/playwright/tests/data-extractor.spec.ts b/playwright/tests/data-extractor.spec.ts index 55296b1cb4..5b18869a71 100644 --- a/playwright/tests/data-extractor.spec.ts +++ b/playwright/tests/data-extractor.spec.ts @@ -36,9 +36,7 @@ test('loads and saves the configuration', async ({ page, utils }) => { await page.locator('[data-test=name-input-save-config-dialog]').fill(config) await page.locator('button:has-text("Ok")').click() // Clear the success toast - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} + await page.getByRole('button', { name: 'Dismiss' }).click() await expect(page.locator('tbody > tr')).toHaveCount(2) await page.locator('[data-test=delete-all]').click() @@ -51,9 +49,7 @@ test('loads and saves the configuration', async ({ page, utils }) => { await page.locator(`td:has-text("${config}")`).click() await page.locator('button:has-text("Ok")').click() // Clear the success toast - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) {} + await page.getByRole('button', { name: 'Dismiss' }).click() await expect(page.locator('tbody > tr')).toHaveCount(2) // Delete this test configuation diff --git a/playwright/tests/data-viewer.spec.ts b/playwright/tests/data-viewer.spec.ts index bd93a7da8d..09a7a2e050 100644 --- a/playwright/tests/data-viewer.spec.ts +++ b/playwright/tests/data-viewer.spec.ts @@ -80,12 +80,7 @@ test('opens and resets the configuration', async ({ page, utils }) => { await page.locator('text=Open Configuration').click() await page.locator(`td:has-text("playwright")`).click() await page.locator('button:has-text("Ok")').click() - await page.getByText('Loading configuration') - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) { - console.error(error) - } + await page.getByRole('button', { name: 'Dismiss' }).click() // Verify the config await page.getByRole('tab', { name: 'Test1' }).click() diff --git a/playwright/tests/limits-monitor.spec.ts b/playwright/tests/limits-monitor.spec.ts index 830c4fe720..f4b6b82795 100644 --- a/playwright/tests/limits-monitor.spec.ts +++ b/playwright/tests/limits-monitor.spec.ts @@ -92,12 +92,7 @@ test('opens and resets the configuration', async ({ page, utils }) => { await page.locator('text=Open Configuration').click() await page.locator(`td:has-text("playwright")`).click() await page.locator('button:has-text("Ok")').click() - await page.getByText('Loading configuration') - try { - await page.getByRole('button', { name: 'Dismiss' }).click() - } catch (error) { - console.error(error) - } + await page.getByRole('button', { name: 'Dismiss' }).click() await page.locator('[data-test=cosmos-limits-monitor-file]').click() await page.locator('text=Show Ignored').click() From 71c495ab5bc133eed24fdb39c8888a13ad4935b6 Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Fri, 20 Oct 2023 18:09:57 -0600 Subject: [PATCH 5/7] api_shared.py testing --- openc3/lib/openc3/script/api_shared.rb | 9 +- openc3/python/openc3/logs/log_writer.py | 63 +- openc3/python/openc3/script/api_shared.py | 93 +- .../python/openc3/utilities/script_shared.py | 12 +- openc3/python/test/api/test_cmd_api.py | 1 + .../protocols/test_template_protocol.py | 14 +- .../test_simulated_target_interface.py | 10 +- openc3/python/test/logs/test_stream_log.py | 1 - .../test_interface_microservice.py | 18 +- openc3/python/test/script/test_api_shared.py | 909 +++++++++++------- openc3/python/test/test_helper.py | 24 +- 11 files changed, 698 insertions(+), 456 deletions(-) diff --git a/openc3/lib/openc3/script/api_shared.rb b/openc3/lib/openc3/script/api_shared.rb index 6d6930bec6..03aa3ac82c 100644 --- a/openc3/lib/openc3/script/api_shared.rb +++ b/openc3/lib/openc3/script/api_shared.rb @@ -171,7 +171,7 @@ def wait(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, token: $op start_time = Time.now.sys openc3_script_sleep() time_diff = Time.now.sys - start_time - Logger.info("WAIT: Indefinite for actual time of #{time_diff} seconds") unless quiet + Logger.info("WAIT: Indefinite for actual time of #{round(time_diff, 3)} seconds") unless quiet # wait(5) # absolute wait time when 1 @@ -179,7 +179,7 @@ def wait(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, token: $op start_time = Time.now.sys openc3_script_sleep(args[0]) time_diff = Time.now.sys - start_time - Logger.info("WAIT: #{args[0]} seconds with actual time of #{time_diff} seconds") unless quiet + Logger.info("WAIT: #{args[0]} seconds with actual time of #{round(time_diff, 3)} seconds") unless quiet else raise "Non-numeric wait time specified" end @@ -245,7 +245,7 @@ def wait_tolerance(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, value.size.times do |i| range = (expected_value[i] - tolerance[i]..expected_value[i] + tolerance[i]) wait_str = "WAIT: #{_upcase(target_name, packet_name, item_name)}[#{i}]" - range_str = "range #{range.first} to #{range.last} with value == #{value[i]} after waiting #{time} seconds" + range_str = "range #{range.first} to #{range.last} with value == #{value[i]} after waiting #{round(time, 3)} seconds" if range.include?(value[i]) message << "#{wait_str} was within #{range_str}\n" else @@ -263,7 +263,7 @@ def wait_tolerance(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, time = Time.now.sys - start_time range = (expected_value - tolerance)..(expected_value + tolerance) wait_str = "WAIT: #{_upcase(target_name, packet_name, item_name)}" - range_str = "range #{range.first} to #{range.last} with value == #{value} after waiting #{time} seconds" + range_str = "range #{range.first} to #{range.last} with value == #{value} after waiting #{round(time, 3)} seconds" if success Logger.info "#{wait_str} was within #{range_str}" unless quiet else @@ -753,6 +753,7 @@ def _openc3_script_wait_implementation(target_name, packet_name, item_name, valu end else begin + puts "value:#{value} eval:#{exp_to_eval}" if eval(exp_to_eval) return true, value end diff --git a/openc3/python/openc3/logs/log_writer.py b/openc3/python/openc3/logs/log_writer.py index 2788476660..fa07b45e28 100644 --- a/openc3/python/openc3/logs/log_writer.py +++ b/openc3/python/openc3/logs/log_writer.py @@ -317,38 +317,37 @@ def close_file(self, take_mutex=True): threads = [] if take_mutex: self.mutex.acquire() - # try: - if self.file: - # try: - self.file.close() - Logger.debug(f"Log File Closed : {self.filename}") - date = self.first_timestamp()[0:8] # YYYYMMDD - bucket_key = os.path.join( - self.remote_log_directory, date, self.bucket_filename() - ) - # Cleanup timestamps here so they are unset for the next file - self.first_time = None - self.last_time = None - threads.append( - BucketUtilities.move_log_file_to_bucket(self.filename, bucket_key) - ) - # Now that the file is in storage, trim the Redis stream after a delay - self.cleanup_offsets.append({}) - for redis_topic, last_offset in self.last_offsets: - self.cleanup_offsets[-1][redis_topic] = last_offset - self.cleanup_times.append( - datetime.now(timezone.utc) + timedelta(seconds=LogWriter.CLEANUP_DELAY) - ) - self.last_offsets.clear - # except RuntimeError as error: - # Logger.error(f"Error closing {self.filename} : {repr(error)}") - - self.file = None - self.file_size = 0 - self.filename = None - # except: - # if take_mutex: - # self.mutex.release() + try: + if self.file: + self.file.close() + Logger.debug(f"Log File Closed : {self.filename}") + date = self.first_timestamp()[0:8] # YYYYMMDD + bucket_key = os.path.join( + self.remote_log_directory, date, self.bucket_filename() + ) + # Cleanup timestamps here so they are unset for the next file + self.first_time = None + self.last_time = None + threads.append( + BucketUtilities.move_log_file_to_bucket(self.filename, bucket_key) + ) + # Now that the file is in storage, trim the Redis stream after a delay + self.cleanup_offsets.append({}) + for redis_topic, last_offset in self.last_offsets: + self.cleanup_offsets[-1][redis_topic] = last_offset + self.cleanup_times.append( + datetime.now(timezone.utc) + + timedelta(seconds=LogWriter.CLEANUP_DELAY) + ) + self.last_offsets.clear + self.file = None + self.file_size = 0 + self.filename = None + except RuntimeError as error: + Logger.error(f"Error closing {self.filename} : {repr(error)}") + finally: + if take_mutex: + self.mutex.release() return threads def bucket_filename(self): diff --git a/openc3/python/openc3/script/api_shared.py b/openc3/python/openc3/script/api_shared.py index 1b12edd451..a4d55cf0bf 100644 --- a/openc3/python/openc3/script/api_shared.py +++ b/openc3/python/openc3/script/api_shared.py @@ -21,6 +21,7 @@ import sys import time +from contextlib import contextmanager import openc3.script from openc3.utilities.script_shared import openc3_script_sleep from .telemetry import * @@ -31,6 +32,10 @@ DEFAULT_TLM_POLLING_RATE = 0.25 +# NOTE: The formatting applied throughout uses :.Xf meaning X decimal points +# This allows extremely small wait times to simply be displayed 0.000. +# Without the 'f' :.X means display X significant figures + def check(*args, type="CONVERTED", scope="DEFAULT"): """Check the converted value of a telmetry item against a condition @@ -133,7 +138,7 @@ def check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): range_bottom = expected_value[i] - tolerance[i] range_top = expected_value[i] + tolerance[i] check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}[{i}]" - range_str = f"range {round(range_bottom, 2)} to {round(range_top, 2)} with value == {value[i]}" + range_str = f"range {frange(range_bottom)} to {frange(range_top)} with value == {value[i]}" if value[i] >= range_bottom and value[i] <= range_top: message += f"{check_str} was within {range_str}\n" else: @@ -151,7 +156,9 @@ def check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): range_bottom = expected_value - tolerance range_top = expected_value + tolerance check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}" - range_str = f"range {round(range_bottom, 2)} to {round(range_top, 2)} with value == {value}" + range_str = ( + f"range {frange(range_bottom)} to {frange(range_top)} with value == {value}" + ) if value >= range_bottom and value <= range_top: Logger.info(f"{check_str} was within {range_str}") else: @@ -194,7 +201,9 @@ def wait(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): openc3_script_sleep() time_diff = time.time() - start_time if not quiet: - Logger.info(f"WAIT: Indefinite for actual time of {time_diff} seconds") + Logger.info( + f"WAIT: Indefinite for actual time of {time_diff:.3f} seconds" + ) # wait(5) # absolute wait time case 1: @@ -208,7 +217,7 @@ def wait(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): time_diff = time.time() - start_time if not quiet: Logger.info( - f"WAIT: {value} seconds with actual time of {time_diff} seconds" + f"WAIT: {value} seconds with actual time of {time_diff:.3f} seconds" ) # wait('target_name packet_name item_name > 1', timeout, polling_rate) # polling_rate is optional @@ -310,7 +319,7 @@ def wait_tolerance(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): range_bottom = expected_value[i] - tolerance[i] range_top = expected_value[i] + tolerance[i] check_str = f"WAIT: {_upcase(target_name, packet_name, item_name)}[{i}]" - range_str = f"range {range_bottom} to {range_top} with value == {value[i]} after waiting {time_diff} seconds" + range_str = f"range {frange(range_bottom)} to {frange(range_top)} with value == {value[i]} after waiting {time_diff:.3f} seconds" if value[i] >= range_bottom and value[i] <= range_top: message += f"{check_str} was within {range_str}\n" else: @@ -336,7 +345,7 @@ def wait_tolerance(*args, type="CONVERTED", quiet=False, scope=OPENC3_SCOPE): range_bottom = expected_value - tolerance range_top = expected_value + tolerance wait_str = f"WAIT: {_upcase(target_name, packet_name, item_name)}" - range_str = f"range {range_bottom} to {range_top} with value == {value} after waiting {time_diff} seconds" + range_str = f"range {frange(range_bottom)} to {frange(range_top)} with value == {value} after waiting {time_diff:.3f} seconds" if not quiet: if success: Logger.info(f"{wait_str} was within {range_str}") @@ -359,11 +368,11 @@ def wait_expression( if not quiet: if success: Logger.info( - f"WAIT: {exp_to_eval} is TRUE after waiting {time_diff} seconds" + f"WAIT: {exp_to_eval} is TRUE after waiting {time_diff:.3f} seconds" ) else: Logger.warn( - f"WAIT: {exp_to_eval} is FALSE after waiting {time_diff} seconds" + f"WAIT: {exp_to_eval} is FALSE after waiting {time_diff:.3f} seconds" ) return time_diff @@ -393,11 +402,13 @@ def wait_check(*args, type="CONVERTED", scope=OPENC3_SCOPE): timeout, polling_rate, ) + if isinstance(value, str): + value = f"'{value}'" # Show user the check against a quoted string time_diff = time.time() - start_time check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}" if comparison_to_eval: check_str += f" {comparison_to_eval}" - with_value_str = f"with value == {str(value)} after waiting {time_diff} seconds" + with_value_str = f"with value == {value} after waiting {time_diff:.3f} seconds" if success: Logger.info(f"{check_str} success {with_value_str}") else: @@ -413,9 +424,9 @@ def wait_check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): """Wait for the value of a telmetry item to be within a tolerance of a value and then check against the condition. Supports two signatures: - wait_tolerance('target_name packet_name item_name', expected_value, tolerance, timeout, polling_rate) + wait_check_tolerance('target_name packet_name item_name', expected_value, tolerance, timeout, polling_rate) or - wait_tolerance('target_name', 'packet_name', 'item_name', expected_value, tolerance, timeout, polling_rate) + wait_check_tolerance('target_name', 'packet_name', 'item_name', expected_value, tolerance, timeout, polling_rate) """ if type not in ["RAW", "CONVERTED"]: raise RuntimeError(f"Invalid type '{type}' for wait_check_tolerance") @@ -454,8 +465,8 @@ def wait_check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): for i in range(0, len(value)): range_bottom = expected_value[i] - tolerance[i] range_top = expected_value[i] + tolerance[i] - check_str = f"WAIT: {_upcase(target_name, packet_name, item_name)}[{i}]" - range_str = f"range {range_bottom} to {range_top} with value == {value[i]} after waiting {time_diff} seconds" + check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}[{i}]" + range_str = f"range {frange(range_bottom)} to {frange(range_top)} with value == {value[i]} after waiting {time_diff:.3f} seconds" if value[i] >= range_bottom and value[i] <= range_top: message += f"{check_str} was within {range_str}\n" else: @@ -484,7 +495,7 @@ def wait_check_tolerance(*args, type="CONVERTED", scope=OPENC3_SCOPE): range_bottom = expected_value - tolerance range_top = expected_value + tolerance check_str = f"CHECK: {_upcase(target_name, packet_name, item_name)}" - range_str = f"range {range_bottom} to {range_top} with value == {value} after waiting {time_diff} seconds" + range_str = f"range {frange(range_bottom)} to {frange(range_top)} with value == {value} after waiting {time_diff:.3f} seconds" if success: Logger.info(f"{check_str} was within {range_str}") else: @@ -506,9 +517,11 @@ def wait_check_expression( ) time_diff = time.time() - start_time if success: - Logger.info(f"CHECK: {exp_to_eval} is TRUE after waiting {time_diff} seconds") + Logger.info( + f"CHECK: {exp_to_eval} is TRUE after waiting {time_diff:.3f} seconds" + ) else: - message = f"CHECK: {exp_to_eval} is FALSE after waiting {time_diff} seconds" + message = f"CHECK: {exp_to_eval} is FALSE after waiting {time_diff:.3f} seconds" if openc3.script.DISCONNECT: Logger.error(message) else: @@ -552,6 +565,7 @@ def wait_check_packet( ) +@contextmanager def disable_instrumentation(): if openc3.script.RUNNING_SCRIPT: openc3.script.RUNNING_SCRIPT.instance.use_instrumentation = False @@ -729,7 +743,7 @@ def _wait_packet( target_name, packet_name, "RECEIVED_COUNT", scope=scope ) # If the packet has not been received the initial_count could be None - if not initial_count: + if initial_count is None: initial_count = 0 start_time = time.time() success, value = _openc3_script_wait_value( @@ -749,10 +763,10 @@ def _wait_packet( if success: if not quiet: Logger.info( - f"{type}: {target_name.upper()} {packet_name.upper()} received {value - initial_count} times after waiting {time_diff} seconds" + f"{type}: {target_name.upper()} {packet_name.upper()} received {value - initial_count} times after waiting {time_diff:.3f} seconds" ) else: - message = f"{type}: {target_name.upper()} {packet_name.upper()} expected to be received {num_packets} times but only received {value - initial_count} times after waiting {time_diff} seconds" + message = f"{type}: {target_name.upper()} {packet_name.upper()} expected to be received {num_packets} times but only received {value - initial_count} times after waiting {time_diff:.3f} seconds" if check: if openc3.script.DISCONNECT: Logger.error(message) @@ -791,7 +805,7 @@ def _execute_wait( wait_str = ( f"WAIT: {_upcase(target_name, packet_name, item_name)} {comparison_to_eval}" ) - value_str = f"with value == {value} after waiting {time_diff} seconds" + value_str = f"with value == {value} after waiting {time_diff:.3f} seconds" if not quiet: if success: Logger.info(f"{wait_str} success {value_str}") @@ -926,8 +940,13 @@ def _openc3_script_wait( value = getattr(openc3.script.API_SERVER, "tlm")( target_name, packet_name, item_name, type=value_type, scope=scope ) - if eval(exp_to_eval): - return True, value + try: + if eval(exp_to_eval): + return True, value + # We get TypeError when trying to eval None >= 0 (for example) + # In this case we just continue and see if eventually we get a good value from tlm() + except TypeError: + pass if time.time() >= end_time: break @@ -944,9 +963,13 @@ def _openc3_script_wait( value = getattr(openc3.script.API_SERVER, "tlm")( target_name, packet_name, item_name, type=value_type, scope=scope ) - if eval(exp_to_eval): - return True, value - else: + try: + if eval(exp_to_eval): + return True, value + else: + return False, value + # We get TypeError when trying to eval None >= 0 (for example) + except TypeError: return False, value except NameError as error: @@ -1025,7 +1048,7 @@ def _openc3_script_wait_array_tolerance( statements = [] for i in range(array_size): statements.append( - f"(value >= ({expected_value[i]} - {abs(tolerance[i])}) and value <= ({expected_value[i]} + {abs(tolerance[i])})" + f"(value[{i}] >= ({expected_value[i]} - {abs(tolerance[i])}) and value[{i}] <= ({expected_value[i]} + {abs(tolerance[i])}))" ) exp_to_eval = " and ".join(statements) return _openc3_script_wait( @@ -1105,3 +1128,21 @@ def _check_eval(target_name, packet_name, item_name, comparison_to_eval, value): f"Uninitialized constant {parts[1]}. Did you mean '{parts[1]}' as a string?" ) raise new_error from error + + +def frange(value): + if isinstance(value, float): + # Display at most 6 significant figures on a range value + # This truncates float values like 1.6500000000000001 to simply 1.65 + return f"{value:.6}" + else: + return value + + +# Interesting formatter to a specific number of significant digits: +# https://stackoverflow.com/questions/3410976/how-to-round-a-number-to-significant-figures-in-python?rq=3 +# def format(value, sigfigs=9): +# if isinstance(value, float): +# return "{:.{p}g}".format(float("{:.{p}g}".format(value, p=sigfigs)), p=sigfigs) +# else: +# return value diff --git a/openc3/python/openc3/utilities/script_shared.py b/openc3/python/openc3/utilities/script_shared.py index bbcb6e0722..e570970092 100644 --- a/openc3/python/openc3/utilities/script_shared.py +++ b/openc3/python/openc3/utilities/script_shared.py @@ -19,16 +19,8 @@ # Defined here but overriden by running_script.py def openc3_script_sleep(sleep_time=None): - time.sleep(sleep_time) - - -# def prompt_for_hazardous(target_name, cmd_name, hazardous_description): -# message = f"Warning: Command {target_name} {cmd_name} is Hazardous. " -# if hazardous_description: -# message += f"\n{hazardous_description}\n" -# message += f"Send? (y): " -# print(message) -# return True + if sleep_time: + time.sleep(float(sleep_time)) def prompt_for_hazardous(target_name, cmd_name, hazardous_description): diff --git a/openc3/python/test/api/test_cmd_api.py b/openc3/python/test/api/test_cmd_api.py index 8ee16c1d2d..dd726df2a9 100644 --- a/openc3/python/test/api/test_cmd_api.py +++ b/openc3/python/test/api/test_cmd_api.py @@ -666,6 +666,7 @@ class BuildCommand(unittest.TestCase): def setUp(self, mock_let, mock_system): redis = mock_redis(self) setup_system() + mock_s3(self) orig_xread = redis.xread diff --git a/openc3/python/test/interfaces/protocols/test_template_protocol.py b/openc3/python/test/interfaces/protocols/test_template_protocol.py index 5102a59b70..0bcf5a9e21 100644 --- a/openc3/python/test/interfaces/protocols/test_template_protocol.py +++ b/openc3/python/test/interfaces/protocols/test_template_protocol.py @@ -224,7 +224,7 @@ def test_processes_responses_with_no_id_fields(self, mock_system): pc = PacketConfig() pc.process_file(tf.name, "SYSTEM") tf.close() - mock_system.telemetry = Telemetry(pc, mock) + mock_system.telemetry = Telemetry(pc, mock_system) self.interface.stream = TestTemplateProtocol.TemplateStream() self.interface.add_protocol( @@ -275,7 +275,7 @@ def test_sets_the_response_id_to_the_defined_id_value(self, mock_system): pc = PacketConfig() pc.process_file(tf.name, "SYSTEM") tf.close() - mock_system.telemetry = Telemetry(pc, mock) + mock_system.telemetry = Telemetry(pc, mock_system) self.interface.stream = TestTemplateProtocol.TemplateStream() self.interface.add_protocol( @@ -333,7 +333,7 @@ def test_handles_multiple_response_ids(self, mock_system): pc = PacketConfig() pc.process_file(tf.name, "SYSTEM") tf.close() - mock_system.telemetry = Telemetry(pc, mock) + mock_system.telemetry = Telemetry(pc, mock_system) self.interface.stream = TestTemplateProtocol.TemplateStream() self.interface.add_protocol( @@ -399,7 +399,7 @@ def test_handles_templates_with_more_values_than_the_response(self, mock_system) pc = PacketConfig() pc.process_file(tf.name, "SYSTEM") tf.close() - mock_system.telemetry = Telemetry(pc, mock) + mock_system.telemetry = Telemetry(pc, mock_system) self.interface.stream = TestTemplateProtocol.TemplateStream() self.interface.add_protocol( @@ -452,7 +452,7 @@ def test_handles_responses_with_more_values_than_the_template(self, mock_system) pc = PacketConfig() pc.process_file(tf.name, "SYSTEM") tf.close() - mock_system.telemetry = Telemetry(pc, mock) + mock_system.telemetry = Telemetry(pc, mock_system) self.interface.stream = TestTemplateProtocol.TemplateStream() self.interface.add_protocol( @@ -508,7 +508,7 @@ def test_ignores_response_lines(self, mock_system): pc = PacketConfig() pc.process_file(tf.name, "SYSTEM") tf.close() - mock_system.telemetry = Telemetry(pc, mock) + mock_system.telemetry = Telemetry(pc, mock_system) self.interface.stream = TestTemplateProtocol.TemplateStream() self.interface.add_protocol(TemplateProtocol, ["0xAD", "0xA", 1], "READ_WRITE") @@ -554,7 +554,7 @@ def test_allows_multiple_response_lines(self, mock_system): pc = PacketConfig() pc.process_file(tf.name, "SYSTEM") tf.close() - mock_system.telemetry = Telemetry(pc, mock) + mock_system.telemetry = Telemetry(pc, mock_system) self.interface.stream = TestTemplateProtocol.TemplateStream() self.interface.add_protocol( diff --git a/openc3/python/test/interfaces/test_simulated_target_interface.py b/openc3/python/test/interfaces/test_simulated_target_interface.py index 9b32b2a914..3d524879d4 100644 --- a/openc3/python/test/interfaces/test_simulated_target_interface.py +++ b/openc3/python/test/interfaces/test_simulated_target_interface.py @@ -22,16 +22,16 @@ from openc3.interfaces.protocols.protocol import Protocol -class TestProtocol(Protocol): +class MyProtocol(Protocol): data = None packet = None def read_data(self, data): - TestProtocol.data = data + MyProtocol.data = data return data def read_packet(self, packet): - TestProtocol.packet = packet + MyProtocol.packet = packet return packet @@ -107,7 +107,7 @@ def test_disconnects_from_the_simulator(self): def test_handles_packet_protocols(self): sti = SimulatedTargetInterface("test/interfaces/sim_tgt_inst.py") sti.target_names = ["INST"] - sti.add_protocol(TestProtocol, [], "READ_WRITE") + sti.add_protocol(MyProtocol, [], "READ_WRITE") sti.connect() pkt = sti.read() - self.assertEqual(TestProtocol.packet, pkt) + self.assertEqual(MyProtocol.packet, pkt) diff --git a/openc3/python/test/logs/test_stream_log.py b/openc3/python/test/logs/test_stream_log.py index d5b5721d3e..2607c569fa 100644 --- a/openc3/python/test/logs/test_stream_log.py +++ b/openc3/python/test/logs/test_stream_log.py @@ -26,7 +26,6 @@ class TestStreamLog(unittest.TestCase): def setUp(self): mock_redis(self) self.mock = mock_s3(self) - self.mock.clear() def tearDown(self) -> None: if hasattr(self, "stream_log"): diff --git a/openc3/python/test/microservices/test_interface_microservice.py b/openc3/python/test/microservices/test_interface_microservice.py index 7ed588868a..f330b89d49 100644 --- a/openc3/python/test/microservices/test_interface_microservice.py +++ b/openc3/python/test/microservices/test_interface_microservice.py @@ -32,7 +32,7 @@ # This must be here in order to work when running more than this individual file -class TestInterface(Interface): +class MyInterface(Interface): read_allow_raise = False connect_raise = False disconnect_count = 0 @@ -45,7 +45,7 @@ def __init__(self, hostname="default", port=12345): super().__init__() def read_allowed(self): - if TestInterface.read_allowed_raise: + if MyInterface.read_allowed_raise: raise RuntimeError("test-error") super().read_allowed() @@ -54,7 +54,7 @@ def connect(self): super().connect() self.data = b"\x00" self.connected = True - if TestInterface.connect_raise: + if MyInterface.connect_raise: raise RuntimeError("test-error") def connected(self): @@ -62,15 +62,15 @@ def connected(self): def disconnect(self): time.sleep(0.001) - TestInterface.disconnect_count += 1 + MyInterface.disconnect_count += 1 self.data = None # Upon disconnect the read_interface should return None - time.sleep(TestInterface.disconnect_delay) + time.sleep(MyInterface.disconnect_delay) self.connected = False super().disconnect() def read_interface(self): time.sleep(0.001) - if TestInterface.read_interface_raise: + if MyInterface.read_interface_raise: raise RuntimeError("test-error") time.sleep(0.1) return self.data @@ -101,7 +101,7 @@ def setUp(self): self.patch_get_class = patch( "openc3.models.interface_model.get_class_from_module", - return_value=TestInterface, + return_value=MyInterface, ) self.patch_get_class.start() self.addCleanup(self.patch_get_class.stop) @@ -176,7 +176,7 @@ def test_creates_an_interface_updates_status_and_starts_cmd_thread(self): # time.sleep(1.1) # Allow threads to exit # def test_handles_exceptions_in_connect(self): - # TestInterface.connect_raise = True + # MyInterface.connect_raise = True # im = InterfaceMicroservice("DEFAULT__INTERFACE__INST_INT") # # all = InterfaceStatusModel.all(scope="DEFAULT") # # self.assertEqual(all["INST_INT"]["state"], "ATTEMPTING") @@ -186,7 +186,7 @@ def test_creates_an_interface_updates_status_and_starts_cmd_thread(self): # thread.start() # time.sleep(1) - # TestInterface.connect_raise = False + # MyInterface.connect_raise = False # time.sleep(1) # all = InterfaceStatusModel.all(scope="DEFAULT") # self.assertEqual(all["INST_INT"]["state"], "CONNECTED") diff --git a/openc3/python/test/script/test_api_shared.py b/openc3/python/test/script/test_api_shared.py index 92364f9155..f007f4faad 100644 --- a/openc3/python/test/script/test_api_shared.py +++ b/openc3/python/test/script/test_api_shared.py @@ -18,11 +18,17 @@ from unittest.mock import * from test.test_helper import * from openc3.script.api_shared import * -from openc3.models.target_model import TargetModel + +cancel = False +count = True +received_count = 0 class Proxy: - def tlm(target_name, packet_name, item_name, type, scope): + def tlm(target_name, packet_name, item_name, type="CONVERTED", scope="DEFAULT"): + global count + global received_count + if scope != "DEFAULT": raise RuntimeError(f"Packet '{target_name} {packet_name}' does not exist") match item_name: @@ -45,27 +51,39 @@ def tlm(target_name, packet_name, item_name, type, scope): case "CCSDSSHF": return "FALSE" case "BLOCKTEST": - return b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + return b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" case "ARY": return [2, 3, 4] + case "RECEIVED_COUNT": + if count: + received_count += 1 + print(f"RECEIVED_COUNT:{received_count}") + return received_count + else: + return None case _: return None +# sleep in a script - returns true if canceled mid sleep +def my_openc3_script_sleep(sleep_time=None): + global cancel + return cancel + + @patch("openc3.script.API_SERVER", Proxy) +@patch("openc3.script.api_shared.openc3_script_sleep", my_openc3_script_sleep) class TestApiShared(unittest.TestCase): def setUp(self): + global received_count + global cancel + global count + received_count = 0 + cancel = False + count = True + mock_redis(self) setup_system() - # case 'RECEIVED_COUNT': - # if self.count: - # received_count += 1 - # received_count - # else: - # None - - model = TargetModel(folder_name="INST", name="INST", scope="DEFAULT") - model.create() def test_check_raises_with_invalid_params(self): with self.assertRaisesRegex( @@ -196,7 +214,7 @@ def test_checks_that_a_value_is_within_a_tolerance(self): ) with self.assertRaisesRegex( CheckError, - f"CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5", + "CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5", ): check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1) @@ -297,340 +315,539 @@ def test_checks_that_an_array_value_is_within_multiple_tolerances(self): ): check_tolerance("INST HEALTH_STATUS ARY", 3, [0.1, 0.1, 2, 3]) + def test_checks_that_an_expression_is_true(self): + for stdout in capture_io(): + check_expression("True == True") + self.assertIn("CHECK: True == True is TRUE", stdout.getvalue()) + with self.assertRaisesRegex(CheckError, "CHECK: True == False is FALSE"): + check_expression("True == False") + + def test_check_expression_logs_instead_of_raises_when_disconnected(self): + openc3.script.DISCONNECT = True + for stdout in capture_io(): + check_expression("True == False") + self.assertIn("CHECK: True == False is FALSE", stdout.getvalue()) + openc3.script.DISCONNECT = False + + def test_checks_a_logical_expression(self): + for stdout in capture_io(): + check_expression("'STRING' == 'STRING'") + self.assertIn("CHECK: 'STRING' == 'STRING' is TRUE", stdout.getvalue()) + with self.assertRaisesRegex(CheckError, "CHECK: 1 == 2 is FALSE"): + check_expression("1 == 2") + with self.assertRaisesRegex( + NameError, + "Uninitialized constant STRING. Did you mean 'STRING' as a string?", + ): + check_expression("'STRING' == STRING") + + def test_waits_for_an_indefinite_time(self): + for stdout in capture_io(): + wait() + self.assertIn( + "WAIT: Indefinite for actual time of 0.000 seconds", stdout.getvalue() + ) + + def test_waits_for_a_relative_time(self): + for stdout in capture_io(): + wait(0.2) + self.assertIn( + "WAIT: 0.2 seconds with actual time of 0.000", stdout.getvalue() + ) + + def test_raises_on_a_non_numeric_time(self): + with self.assertRaisesRegex(RuntimeError, "Non-numeric wait time specified"): + wait("LONG") + + def test_waits_for_a_tgt_pkt_item(self): + for stdout in capture_io(): + wait("INST HEALTH_STATUS TEMP1 > 0", 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS TEMP1 > 0 success with value == 10 after waiting 0.0", + stdout.getvalue(), + ) + + wait("INST HEALTH_STATUS TEMP1 < 0", 0.1, 0.1) # Last param is polling rate + self.assertIn( + "WAIT: INST HEALTH_STATUS TEMP1 < 0 failed with value == 10 after waiting 0.1", + stdout.getvalue(), + ) + + wait("INST", "HEALTH_STATUS", "TEMP1", "> 0", 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS TEMP1 > 0 success with value == 10 after waiting 0.0", + stdout.getvalue(), + ) + + wait( + "INST", "HEALTH_STATUS", "TEMP1", "== 0", 0.1, 0.1 + ) # Last param is polling rate + self.assertIn( + "WAIT: INST HEALTH_STATUS TEMP1 == 0 failed with value == 10 after waiting 0.1", + stdout.getvalue(), + ) + + with self.assertRaisesRegex(RuntimeError, "Invalid number of arguments"): + wait("INST", "HEALTH_STATUS", "TEMP1", "== 0", 0.1, 0.1, 0.1) + + def test_wait_tolerance_raises_with_invalid_params(self): + with self.assertRaisesRegex( + RuntimeError, + r"ERROR: Invalid number of arguments \(3\) passed to wait_tolerance", + ): + wait_tolerance("INST", "HEALTH_STATUS", "TEMP2", type="RAW") + with self.assertRaisesRegex( + RuntimeError, "ERROR: Telemetry Item must be specified" + ): + wait_tolerance("INST", "HEALTH_STATUS", 1.55, 0.1, type="RAW") + + def test_wait_tolerance_raises_with_formatted_or_with_units(self): + with self.assertRaisesRegex( + RuntimeError, "Invalid type 'FORMATTED' for wait_tolerance" + ): + wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type="FORMATTED") + with self.assertRaisesRegex( + RuntimeError, "Invalid type 'WITH_UNITS' for wait_tolerance" + ): + wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type="WITH_UNITS") + + def test_waits_for_a_value_to_be_within_a_tolerance(self): + for stdout in capture_io(): + wait_tolerance("INST", "HEALTH_STATUS", "TEMP2", 1.55, 0.1, 5, type="RAW") + self.assertIn( + "WAIT: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65 with value == 1.5 after waiting 0.0", + stdout.getvalue(), + ) + wait_tolerance("INST HEALTH_STATUS TEMP2", 10.5, 0.01, 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS TEMP2 was within range 10.49 to 10.51 with value == 10.5 after waiting 0.0", + stdout.getvalue(), + ) + wait_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) + self.assertIn( + "WAIT: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5 after waiting 0.1", + stdout.getvalue(), + ) + + def test_waits_that_an_array_value_is_within_a_single_tolerance(self): + for stdout in capture_io(): + wait_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1, 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[1] was within range 2 to 4 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[2] was within range 2 to 4 with value == 4", + stdout.getvalue(), + ) + wait_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) + self.assertIn( + "INST HEALTH_STATUS ARY[0] failed to be within range 2.9 to 3.1 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "INST HEALTH_STATUS ARY[2] failed to be within range 2.9 to 3.1 with value == 4", + stdout.getvalue(), + ) + + def test_waits_that_multiple_array_values_are_within_tolerance(self): + for stdout in capture_io(): + wait_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1, 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4", + stdout.getvalue(), + ) + + wait_tolerance("INST HEALTH_STATUS ARY", [2, 3, 4], 0.1, 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4", + stdout.getvalue(), + ) + + def test_waits_that_an_array_value_is_within_multiple_tolerances(self): + for stdout in capture_io(): + wait_tolerance("INST", "HEALTH_STATUS", "ARY", 3, [1, 0.1, 2], 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4", + stdout.getvalue(), + ) + + wait_tolerance("INST HEALTH_STATUS ARY", 3, [1, 0.1, 2], 5) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4", + stdout.getvalue(), + ) + + def test_waits_for_an_expression(self): + for stdout in capture_io(): + wait_expression("True == True", 5) + self.assertIn( + "WAIT: True == True is TRUE after waiting 0.0", + stdout.getvalue(), + ) + wait_expression("True == False", 0.1) + self.assertIn( + "WAIT: True == False is FALSE after waiting 0.1", stdout.getvalue() + ) + + def test_wats_a_logical_expression(self): + for stdout in capture_io(): + wait_expression("'STRING' == 'STRING'", 5) + self.assertIn( + "WAIT: 'STRING' == 'STRING' is TRUE after waiting 0.0", + stdout.getvalue(), + ) + wait_expression("1 == 2", 0.1) + self.assertIn("WAIT: 1 == 2 is FALSE after waiting 0.1", stdout.getvalue()) + with self.assertRaisesRegex( + NameError, + "Uninitialized constant STRING. Did you mean 'STRING' as a string?", + ): + wait_expression("'STRING' == STRING", 5) + + def test_wait_check_raises_with_invalid_params(self): + with self.assertRaisesRegex( + RuntimeError, + r"ERROR: Invalid number of arguments \(1\) passed to wait_check", + ): + wait_check("INST HEALTH_STATUS TEMP1") + + def test_checks_a_telemetry_item_against_a_value(self): + for stdout in capture_io(): + wait_check( + "INST", "HEALTH_STATUS", "TEMP1", "> 1", 0.01, 0.1 + ) # Last param is polling rate + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 > 1 success with value == 10", + stdout.getvalue(), + ) + wait_check("INST HEALTH_STATUS TEMP1 == 1", 0.01, 0.1, type="RAW") + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 == 1 success with value == 1", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + "CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10 after waiting 0.01", + ): + wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) + + def test_wait_check_logs_instead_of_raises_when_disconnected(self): + openc3.script.DISCONNECT = True + for stdout in capture_io(): + wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10", + stdout.getvalue(), + ) + openc3.script.DISCONNECT = False + + def test_fails_against_binary_data(self): + data = "\xFF" * 10 + with self.assertRaisesRegex( + RuntimeError, "ERROR: Invalid comparison to non-ascii value" + ): + wait_check(f"INST HEALTH_STATUS BLOCKTEST == {data}", 0.01) + data = b"\xFF" * 10 + wait_check(f"INST HEALTH_STATUS BLOCKTEST == {data}", 0.01) + data = "\xFF" * 10 + with self.assertRaisesRegex( + RuntimeError, "ERROR: Invalid comparison to non-ascii value" + ): + wait_check(f"INST HEALTH_STATUS BLOCKTEST == '{data}'", 0.01) + data = b"\xFF" * 10 + with self.assertRaises(SyntaxError): + wait_check(f"INST HEALTH_STATUS BLOCKTEST == '{data}'", 0.01) + + def test_warns_when_checking_a_state_against_a_constant(self): + for stdout in capture_io(): + wait_check("INST HEALTH_STATUS CCSDSSHF == 'FALSE'", 0.01) + self.assertIn( + "CHECK: INST HEALTH_STATUS CCSDSSHF == 'FALSE' success with value == 'FALSE'", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + NameError, + "Uninitialized constant FALSE. Did you mean 'FALSE' as a string?", + ): + wait_check("INST HEALTH_STATUS CCSDSSHF == FALSE", 0.01) + + def test_wait_check_tolerance_raises_with_formatted_or_with_units(self): + with self.assertRaisesRegex( + RuntimeError, r"Invalid type 'FORMATTED' for wait_check_tolerance" + ): + wait_check_tolerance( + "INST HEALTH_STATUS TEMP2 == 10.5", 0.1, 5, type="FORMATTED" + ) + with self.assertRaisesRegex( + RuntimeError, r"Invalid type 'WITH_UNITS' for wait_check_tolerance" + ): + wait_check_tolerance( + "INST HEALTH_STATUS TEMP2 == 10.5", 0.1, 5, type="WITH_UNITS" + ) + + def test_wait_checks_that_a_value_is_within_a_tolerance(self): + for stdout in capture_io(): + wait_check_tolerance( + "INST", "HEALTH_STATUS", "TEMP2", 1.55, 0.1, 5, type="RAW" + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65 with value == 1.5", + stdout.getvalue(), + ) + wait_check_tolerance("INST HEALTH_STATUS TEMP2", 10.5, 0.01, 5) + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP2 was within range 10.49 to 10.51 with value == 10.5", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5", + ): + wait_check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) + + def test_wait_checks_that_an_array_value_is_within_a_single_tolerance(self): + for stdout in capture_io(): + wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1, 5) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[1] was within range 2 to 4 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[2] was within range 2 to 4 with value == 4", + stdout.getvalue(), + ) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2", + ): + wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS ARY\[1\] was within range 2.9 to 3.1 with value == 3", + ): + wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) + with self.assertRaisesRegex( + CheckError, + r"CHECK: INST HEALTH_STATUS ARY\[2\] failed to be within range 2.9 to 3.1 with value == 4", + ): + wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) + + def test_wait_check_tolerance_logs_instead_of_raises_when_disconnected(self): + openc3.script.DISCONNECT = True + for stdout in capture_io(): + wait_check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) + self.assertIn( + "CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5", + stdout.getvalue(), + ) + for stdout in capture_io(): + wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[0] failed to be within range 2.9 to 3.1 with value == 2", + stdout.getvalue(), + ) + openc3.script.DISCONNECT = False + + def test_wait_checks_that_multiple_array_values_are_within_tolerance(self): + for stdout in capture_io(): + wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1, 5) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4", + stdout.getvalue(), + ) + + def test_wait_checks_that_an_array_value_is_within_multiple_tolerances(self): + for stdout in capture_io(): + wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, [1, 0.1, 2], 5) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3", + stdout.getvalue(), + ) + self.assertIn( + "CHECK: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4", + stdout.getvalue(), + ) + + def test_waits_and_checks_that_an_expression_is_true(self): + for stdout in capture_io(): + wait_check_expression("True == True", 5) + self.assertIn("CHECK: True == True is TRUE", stdout.getvalue()) + with self.assertRaisesRegex(CheckError, "CHECK: True == False is FALSE"): + wait_check_expression("True == False", 0.1) + + def test_wait_check_expression_logs_instead_of_raises_when_disconnected(self): + openc3.script.DISCONNECT = True + for stdout in capture_io(): + wait_check_expression("True == False", 5) + self.assertIn("CHECK: True == False is FALSE", stdout.getvalue()) + openc3.script.DISCONNECT = False + + def test_waits_and_checks_a_logical_expression(self): + for stdout in capture_io(): + wait_check_expression("'STRING' == 'STRING'", 5) + self.assertIn("CHECK: 'STRING' == 'STRING' is TRUE", stdout.getvalue()) + with self.assertRaisesRegex(CheckError, "CHECK: 1 == 2 is FALSE"): + wait_check_expression("1 == 2", 0.1) + with self.assertRaisesRegex( + NameError, + "Uninitialized constant STRING. Did you mean 'STRING' as a string?", + ): + wait_check_expression("'STRING' == STRING", 0.1) + + def test_wait_packet_prints_warning_if_packet_not_received(self): + global count + count = False + for stdout in capture_io(): + wait_packet("INST", "HEALTH_STATUS", 1, 0.5) + self.assertIn( + "WAIT: INST HEALTH_STATUS expected to be received 1 times but only received 0 times", + stdout.getvalue(), + ) + + def test_wait_packet_prints_success_if_the_packet_is_received(self): + global cancel + global count + count = True + for stdout in capture_io(): + cancel = True + wait_packet("INST", "HEALTH_STATUS", 5, 0.5) + self.assertIn( + "WAIT: INST HEALTH_STATUS expected to be received 5 times", + stdout.getvalue(), + ) + cancel = False + wait_packet("INST", "HEALTH_STATUS", 5, 0.5) + self.assertIn( + "WAIT: INST HEALTH_STATUS received 5 times after waiting", + stdout.getvalue(), + ) + + def test_wait_check_packet_raises_a_check_error_if_packet_not_received(self): + global count + count = False + with self.assertRaisesRegex( + CheckError, + "CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times", + ): + wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) + + def test_wait_check_packet_logs_instead_of_raises_if_disconnected(self): + openc3.script.DISCONNECT = True + global count + count = False + for stdout in capture_io(): + wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) + self.assertIn( + "CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times", + stdout.getvalue(), + ) + openc3.script.DISCONNECT = False + + def test_wait_check_packet_prints_success_if_the_packet_is_received(self): + global cancel + global count + count = True + for stdout in capture_io(): + cancel = True + with self.assertRaisesRegex( + CheckError, + "CHECK: INST HEALTH_STATUS expected to be received 5 times", + ): + wait_check_packet("INST", "HEALTH_STATUS", 5, 0.0) + cancel = False + wait_check_packet("INST", "HEALTH_STATUS", 5, 0.5) + self.assertIn( + "CHECK: INST HEALTH_STATUS received 5 times after waiting", + stdout.getvalue(), + ) + + def test_does_nothing_if_runningscript_is_not_defined(self): + for stdout in capture_io(): + with disable_instrumentation(): + print("HI") + self.assertIn("HI", stdout.getvalue()) + + def test_sets_runningscript_instance_use_instrumentation(self): + openc3.script.RUNNING_SCRIPT = Mock() + for stdout in capture_io(): + self.assertTrue(openc3.script.RUNNING_SCRIPT.instance.use_instrumentation) + with disable_instrumentation(): + self.assertFalse( + openc3.script.RUNNING_SCRIPT.instance.use_instrumentation + ) + print("HI") + self.assertTrue(openc3.script.RUNNING_SCRIPT.instance.use_instrumentation) + self.assertIn("HI", stdout.getvalue()) + + def test_gets_and_sets_runningscript_line_delay(self): + openc3.script.RUNNING_SCRIPT = Mock() + set_line_delay(10) + self.assertEqual(openc3.script.RUNNING_SCRIPT.line_delay, 10) + self.assertEqual(get_line_delay(), 10) + + def test_gets_and_sets_runningscript_max_output_characters(self): + openc3.script.RUNNING_SCRIPT = Mock() + set_max_output(100) + self.assertEqual(openc3.script.RUNNING_SCRIPT.max_output_characters, 100) + self.assertEqual(get_max_output(), 100) -# class CheckToleranceRaw(unittest.TestCase): -# def test_checks_that_a_value_is_within_a_tolerance(self): -# for stdout in capture_io(): -# check_tolerance_raw("INST HEALTH_STATUS TEMP2", 1.55, 0.1) -# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65\d+ with value == 1.5', stdout.getvalue()) - -# class CheckExpression(unittest.TestCase): -# def test_checks_that_an_expression_is_True(self): -# for stdout in capture_io(): -# check_expression("True == True") -# self.assertIn('CHECK: True == True is TRUE', stdout.getvalue()) -# { check_expression("True == False") }.to raise_error(/CHECK: True == False is FALSE/) - -# def test_logs_instead_of_raises_when_disconnected(self): -# openc3.script.DISCONNECT = True -# for stdout in capture_io(): -# check_expression("True == False") -# self.assertIn('CHECK: True == False is FALSE', stdout.getvalue()) -# openc3.script.DISCONNECT = False - -# def test_checks_a_logical_expression(self): -# for stdout in capture_io(): -# check_expression("'STRING' == 'STRING'") -# self.assertIn('CHECK: 'STRING' == 'STRING' is TRUE', stdout.getvalue()) -# { check_expression("1 == 2") }.to raise_error(/CHECK: 1 == 2 is FALSE/) -# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant STRING. Did you mean 'STRING' as a string?"): -# check_expression("'STRING' == STRING") - -# class Wait(unittest.TestCase): -# def test_waits_for_an_indefinite_time(self): -# for stdout in capture_io(): -# wait() -# self.assertIn('WAIT: Indefinite for actual time of .* seconds', stdout.getvalue()) - -# def test_waits_for_a_relative_time(self): -# for stdout in capture_io(): -# wait(5) -# self.assertIn('WAIT: 5 seconds with actual time of .* seconds', stdout.getvalue()) - -# def test_raises_on_a_non_numeric_time(self): -# { wait('5') }.to raise_error("Non-numeric wait time specified") - -# def test_waits_for_a_tgt_pkt_item(self): -# for stdout in capture_io(): -# wait("INST HEALTH_STATUS TEMP1 > 0", 5) -# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 > 0 success with value == 10 after waiting .* seconds', stdout.getvalue()) - -# wait("INST HEALTH_STATUS TEMP1 < 0", 0.1, 0.1) # Last param is polling rate -# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 < 0 failed with value == 10 after waiting .* seconds', stdout.getvalue()) - -# wait("INST", "HEALTH_STATUS", "TEMP1", "> 0", 5) -# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 > 0 success with value == 10 after waiting .* seconds', stdout.getvalue()) - -# wait("INST", "HEALTH_STATUS", "TEMP1", "== 0", 0.1, 0.1) # Last param is polling rate -# self.assertIn('WAIT: INST HEALTH_STATUS TEMP1 == 0 failed with value == 10 after waiting .* seconds', stdout.getvalue()) - -# { wait("INST", "HEALTH_STATUS", "TEMP1", "== 0", 0.1, 0.1, 0.1) }.to raise_error(/Invalid number of arguments/) - - -# class WaitTolerance(unittest.TestCase): -# def test_raises_with_invalid_params(self): -# { wait_tolerance("INST", "HEALTH_STATUS", "TEMP2", type= 'RAW') }.to raise_error(/ERROR: Invalid number of arguments \(3\) passed to wait_tolerance/) -# { wait_tolerance("INST", "HEALTH_STATUS", 1.55, 0.1, type= 'RAW') }.to raise_error(/ERROR: Telemetry Item must be specified/) - -# def test_raises_with_=formatted_or_=with_units(self): -# { wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type= 'FORMATTED') }.to raise_error("Invalid type 'FORMATTED' for wait_tolerance") -# { wait_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, type= 'WITH_UNITS') }.to raise_error("Invalid type 'WITH_UNITS' for wait_tolerance") - -# def test_waits_for_a_value_to_be_within_a_tolerance(self): -# for stdout in capture_io(): -# wait_tolerance("INST", "HEALTH_STATUS", "TEMP2", 1.55, 0.1, 5, type= 'RAW') -# self.assertIn('WAIT: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65\d+ with value == 1.5', stdout.getvalue()) -# wait_tolerance("INST HEALTH_STATUS TEMP2", 10.5, 0.01, 5) -# self.assertIn(["WAIT: INST HEALTH_STATUS TEMP2 was within range 10.49 to 10.51 with value == 10.5"], stdout.getvalue()) -# wait_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) -# self.assertIn(["WAIT: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5"], stdout.getvalue()) - -# def test_checks_that_an_array_value_is_within_a_single_tolerance(self): -# for stdout in capture_io(): -# wait_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1, 5) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2 to 4 with value == 3"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 2 to 4 with value == 4"], stdout.getvalue()) -# wait_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) -# self.assertIn(["INST HEALTH_STATUS ARY[0] failed to be within range 2.9 to 3.1 with value == 2"], stdout.getvalue()) -# self.assertIn(["INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) -# self.assertIn(["INST HEALTH_STATUS ARY[2] failed to be within range 2.9 to 3.1 with value == 4"], stdout.getvalue()) - -# def test_checks_that_multiple_array_values_are_within_tolerance(self): -# for stdout in capture_io(): -# wait_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1, 5) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4"], stdout.getvalue()) - -# wait_tolerance("INST HEALTH_STATUS ARY", [2, 3, 4], 0.1, 5) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4"], stdout.getvalue()) - -# def test_checks_that_an_array_value_is_within_multiple_tolerances(self): -# for stdout in capture_io(): -# wait_tolerance("INST", "HEALTH_STATUS", "ARY", 3, [1, 0.1, 2], 5) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4"], stdout.getvalue()) - -# wait_tolerance("INST HEALTH_STATUS ARY", 3, [1, 0.1, 2], 5) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) -# self.assertIn(["WAIT: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4"], stdout.getvalue()) - -# class WaitExpression(unittest.TestCase): -# [True, False].each do |cancel| -# context "with wait cancelled {cancel}" do -# def test_waits_for_an_expression(self): -# self.sleep_cancel = cancel -# for stdout in capture_io(): -# wait_expression("True == True", 5) -# self.assertIn('WAIT: True == True is TRUE after waiting .* seconds', stdout.getvalue()) -# wait_expression("True == False", 0.1) -# self.assertIn('WAIT: True == False is FALSE after waiting .* seconds', stdout.getvalue()) - -# def test_checks_a_logical_expression(self): -# for stdout in capture_io(): -# wait_expression("'STRING' == 'STRING'", 5) -# self.assertIn('WAIT: 'STRING' == 'STRING' is TRUE after waiting .* seconds', stdout.getvalue()) -# wait_expression("1 == 2", 0.1) -# self.assertIn('WAIT: 1 == 2 is FALSE after waiting .* seconds', stdout.getvalue()) -# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant STRING. Did you mean 'STRING' as a string?"): -# wait_expression("'STRING' == STRING", 5) - -# class WaitCheck(unittest.TestCase): -# def test_raises_with_invalid_params(self): -# { wait_check("INST HEALTH_STATUS TEMP1") }.to raise_error(/ERROR: Invalid number of arguments \(1\) passed to wait_check/) - -# def test_checks_a_telemetry_item_against_a_value(self): -# for stdout in capture_io(): -# wait_check("INST", "HEALTH_STATUS", "TEMP1", "> 1", 0.01, 0.1) # Last param is polling rate -# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 > 1 success with value == 10'], stdout.getvalue()) -# wait_check("INST HEALTH_STATUS TEMP1 == 1", 0.01, 0.1, type= 'RAW') -# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 == 1 success with value == 1'], stdout.getvalue()) -# { wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) }.to raise_error(/CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10/) - -# [True, False].each do |cancel| -# context "with wait cancelled {cancel}" do -# def test_handles_a_block(self): -# self.sleep_cancel = cancel - -# for stdout in capture_io(): -# wait_check("INST HEALTH_STATUS TEMP1", 0.01) do |value| -# value == 10 -# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 success with value == 10'], stdout.getvalue()) - -# { -# wait_check("INST HEALTH_STATUS TEMP1", 0.01) do |value| -# value == 1 -# }.to raise_error(/CHECK: INST HEALTH_STATUS TEMP1 failed with value == 10/) - -# def test_logs_instead_of_raises_when_disconnected(self): -# openc3.script.DISCONNECT = True -# for stdout in capture_io(): -# wait_check("INST HEALTH_STATUS TEMP1 > 100", 0.01) -# self.assertIn(['CHECK: INST HEALTH_STATUS TEMP1 > 100 failed with value == 10'], stdout.getvalue()) -# openc3.script.DISCONNECT = False - -# def test_fails_against_binary_data(self): -# data = "\xFF" * 10 -# with self.assertRaisesRegex(AttributeError, f"ERROR: Invalid comparison to non-ascii value"): -# wait_check("INST HEALTH_STATUS BLOCKTEST == '{data}'", 0.01) - -# def test_warns_when_checking_a_state_against_a_constant(self): -# for stdout in capture_io(): -# wait_check("INST HEALTH_STATUS CCSDSSHF == 'FALSE'", 0.01) -# self.assertIn(["CHECK: INST HEALTH_STATUS CCSDSSHF == 'FALSE' success with value == 'FALSE'"], stdout.getvalue()) -# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant FALSE. Did you mean 'FALSE' as a string?"): -# wait_check("INST HEALTH_STATUS CCSDSSHF == FALSE", 0.01) - -# class WaitCheckTolerance(unittest.TestCase): -# def test_raises_with_=formatted_or_=with_units(self): -# { wait_check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, 5, type= 'FORMATTED') }.to raise_error("Invalid type 'FORMATTED' for wait_check_tolerance") -# { wait_check_tolerance("INST HEALTH_STATUS TEMP2 == 10.5", 0.1, 5, type= 'WITH_UNITS') }.to raise_error("Invalid type 'WITH_UNITS' for wait_check_tolerance") - -# def test_checks_that_a_value_is_within_a_tolerance(self): -# for stdout in capture_io(): -# wait_check_tolerance("INST", "HEALTH_STATUS", "TEMP2", 1.55, 0.1, 5, type= 'RAW') -# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 was within range 1.45 to 1.65\d+ with value == 1.5', stdout.getvalue()) -# wait_check_tolerance("INST HEALTH_STATUS TEMP2", 10.5, 0.01, 5) -# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 was within range 10.49 to 10.51 with value == 10.5', stdout.getvalue()) -# with self.assertRaisesRegex(AttributeError, f"CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5"): -# wait_check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) - -# def test_checks_that_an_array_value_is_within_a_single_tolerance(self): -# for stdout in capture_io(): -# wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, 1, 5) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[1] was within range 2 to 4 with value == 3"], stdout.getvalue()) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[2] was within range 2 to 4 with value == 4"], stdout.getvalue()) -# { wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2/) -# { wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[1\] was within range 2.9 to 3.1 with value == 3/) -# { wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) }.to raise_error(/INST HEALTH_STATUS ARY\[2\] failed to be within range 2.9 to 3.1 with value == 4/) - -# def test_logs_instead_of_raises_when_disconnected(self): -# openc3.script.DISCONNECT = True -# for stdout in capture_io(): -# wait_check_tolerance("INST HEALTH_STATUS TEMP2", 11, 0.1, 0.1) -# self.assertIn('CHECK: INST HEALTH_STATUS TEMP2 failed to be within range 10.9 to 11.1 with value == 10.5', stdout.getvalue()) -# for stdout in capture_io(): -# wait_check_tolerance("INST HEALTH_STATUS ARY", 3, 0.1, 0.1) -# self.assertIn('CHECK: INST HEALTH_STATUS ARY\[0\] failed to be within range 2.9 to 3.1 with value == 2', stdout.getvalue()) -# openc3.script.DISCONNECT = False - -# def test_checks_that_multiple_array_values_are_within_tolerance(self): -# for stdout in capture_io(): -# wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", [2, 3, 4], 0.1, 5) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[0] was within range 1.9 to 2.1 with value == 2"], stdout.getvalue()) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[2] was within range 3.9 to 4.1 with value == 4"], stdout.getvalue()) - -# def test_checks_that_an_array_value_is_within_multiple_tolerances(self): -# for stdout in capture_io(): -# wait_check_tolerance("INST", "HEALTH_STATUS", "ARY", 3, [1, 0.1, 2], 5) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[0] was within range 2 to 4 with value == 2"], stdout.getvalue()) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[1] was within range 2.9 to 3.1 with value == 3"], stdout.getvalue()) -# self.assertIn(["CHECK: INST HEALTH_STATUS ARY[2] was within range 1 to 5 with value == 4"], stdout.getvalue()) - -# class WaitCheckExpression(unittest.TestCase): -# def test_waits_and_checks_that_an_expression_is_True(self): -# for stdout in capture_io(): -# wait_check_expression("True == True", 5) -# self.assertIn('CHECK: True == True is TRUE', stdout.getvalue()) -# { wait_check_expression("True == False", 0.1) }.to raise_error(/CHECK: True == False is FALSE/) - -# def test_logs_instead_of_raises_when_disconnected(self): -# openc3.script.DISCONNECT = True -# for stdout in capture_io(): -# wait_check_expression("True == False", 5) -# self.assertIn('CHECK: True == False is FALSE', stdout.getvalue()) -# openc3.script.DISCONNECT = False - -# def test_waits_and_checks_a_logical_expression(self): -# for stdout in capture_io(): -# wait_check_expression("'STRING' == 'STRING'", 5) -# self.assertIn('CHECK: 'STRING' == 'STRING' is TRUE', stdout.getvalue()) -# { wait_check_expression("1 == 2", 0.1) }.to raise_error(/CHECK: 1 == 2 is FALSE/) -# with self.assertRaisesRegex(AttributeError, f"Uninitialized constant STRING. Did you mean 'STRING' as a string?"): -# wait_check_expression("'STRING' == STRING", 0.1) - -# [True, False].each do |cancel| -# context "with wait cancelled {cancel}" do - -# class WaitPacket(unittest.TestCase): -# def setUp(self): -# self.sleep_cancel = cancel - -# def test_prints_warning_if_packet_not_received(self): -# self.count = False -# for stdout in capture_io(): -# wait_packet("INST", "HEALTH_STATUS", 1, 0.5) -# self.assertIn('WAIT: INST HEALTH_STATUS expected to be received 1 times but only received 0 times', stdout.getvalue()) - -# def test_prints_success_if_the_packet_is_received(self): -# self.count = True -# for stdout in capture_io(): -# wait_packet("INST", "HEALTH_STATUS", 5, 0.5) -# if cancel: -# self.assertIn('WAIT: INST HEALTH_STATUS expected to be received 5 times', stdout.getvalue()) -# else: -# self.assertIn('WAIT: INST HEALTH_STATUS received 5 times after waiting', stdout.getvalue()) - -# class WaitCheckPacket(unittest.TestCase): -# def setUp(self): -# self.sleep_cancel = cancel - -# def test_raises_a_check_error_if_packet_not_received(self): -# self.count = False -# { wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) }.to raise_error(/CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times/) - -# def test_logs_instead_of_raises_if_disconnected(self): -# openc3.script.DISCONNECT = True -# self.count = False -# for stdout in capture_io(): -# wait_check_packet("INST", "HEALTH_STATUS", 1, 0.5) -# self.assertIn('CHECK: INST HEALTH_STATUS expected to be received 1 times but only received 0 times', stdout.getvalue()) -# openc3.script.DISCONNECT = False - -# def test_prints_success_if_the_packet_is_received(self): -# self.count = True -# for stdout in capture_io(): -# if cancel: -# { wait_check_packet("INST", "HEALTH_STATUS", 5, 0.5) }.to raise_error(/CHECK: INST HEALTH_STATUS expected to be received 5 times/) -# else: -# wait_check_packet("INST", "HEALTH_STATUS", 5, 0.5) -# self.assertIn('CHECK: INST HEALTH_STATUS received 5 times after waiting', stdout.getvalue()) - -# class DisableInstrumentation(unittest.TestCase): -# def test_does_nothing_if_runningscript_is_not_defined(self): -# for stdout in capture_io(): -# disable_instrumentation do -# puts "HI" -# expect(stdout.getvalue()).to match("HI") - -# def test_sets_runningscript.instance.use_instrumentation(self): -# class RunningScript: -# self.struct = OpenStruct() -# self.struct.use_instrumentation = True -# def self.instance: -# self.struct -# for stdout in capture_io(): -# self.assertTrue(RunningScript.instance.use_instrumentation) -# disable_instrumentation do -# self.assertFalse(RunningScript.instance.use_instrumentation) -# puts "HI" -# self.assertTrue(RunningScript.instance.use_instrumentation) -# expect(stdout.getvalue()).to match("HI") - -# class GetLineDelay, setLineDelay(unittest.TestCase): -# def test_gets_and_sets_runningscript.line_delay(self): -# class RunningScript: -# instance_attr_accessor :line_delay - -# set_line_delay(10) -# self.assertEqual(RunningScript.line_delay, 10) -# self.assertEqual(get_line_delay(), 10) - -# class GetMaxOutput, setMaxOutput(unittest.TestCase): -# def test_gets_and_sets_runningscript.max_output_characters(self): -# class RunningScript: -# instance_attr_accessor :max_output_characters - -# set_max_output(100) -# self.assertEqual(RunningScript.max_output_characters, 100) -# self.assertEqual(get_max_output(), 100) # class Start, loadUtility, requireUtility(unittest.TestCase): # def test_loads_a_script(self): diff --git a/openc3/python/test/test_helper.py b/openc3/python/test/test_helper.py index 5306796dc0..5f46e980d2 100644 --- a/openc3/python/test/test_helper.py +++ b/openc3/python/test/test_helper.py @@ -92,7 +92,7 @@ def mock_redis(self): Store.my_instance = None redis = fakeredis.FakeRedis() patcher = patch("redis.Redis", return_value=redis) - self.mock_redis = patcher.start() + patcher.start() self.addCleanup(patcher.stop) return redis @@ -111,25 +111,17 @@ def clear(self): self.files = {} -mock = MockS3() +# Create a MockS3 to make this a singleton +mocks3 = MockS3() def mock_s3(self): - # We have to remove all the openc3 modules to allow the boto3 mock patch - # to be applied when we use the aws_bucket. There's probably an easier or - # more targeted way to achieve this but I don't know it. To test print - # the s3_session object in aws_bucket __init__. - # TODO: is there a way to use importlib.reload - names = [] - for name, _ in sys.modules.items(): - if "openc3" in name: - names.append(name) - for name in names: - del sys.modules[name] - patcher = patch("boto3.session.Session", return_value=mock) - self.mock_s3 = patcher.start() + # Clear it out everytime it is used + mocks3.clear() + patcher = patch("boto3.session.Session", return_value=mocks3) + patcher.start() self.addCleanup(patcher.stop) - return mock + return mocks3 def capture_io(): From 47f737af44aa260e7850226efef9160db0592fae Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Fri, 20 Oct 2023 20:12:35 -0600 Subject: [PATCH 6/7] Remove debugging --- openc3/lib/openc3/script/api_shared.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/openc3/lib/openc3/script/api_shared.rb b/openc3/lib/openc3/script/api_shared.rb index 03aa3ac82c..6d6930bec6 100644 --- a/openc3/lib/openc3/script/api_shared.rb +++ b/openc3/lib/openc3/script/api_shared.rb @@ -171,7 +171,7 @@ def wait(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, token: $op start_time = Time.now.sys openc3_script_sleep() time_diff = Time.now.sys - start_time - Logger.info("WAIT: Indefinite for actual time of #{round(time_diff, 3)} seconds") unless quiet + Logger.info("WAIT: Indefinite for actual time of #{time_diff} seconds") unless quiet # wait(5) # absolute wait time when 1 @@ -179,7 +179,7 @@ def wait(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, token: $op start_time = Time.now.sys openc3_script_sleep(args[0]) time_diff = Time.now.sys - start_time - Logger.info("WAIT: #{args[0]} seconds with actual time of #{round(time_diff, 3)} seconds") unless quiet + Logger.info("WAIT: #{args[0]} seconds with actual time of #{time_diff} seconds") unless quiet else raise "Non-numeric wait time specified" end @@ -245,7 +245,7 @@ def wait_tolerance(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, value.size.times do |i| range = (expected_value[i] - tolerance[i]..expected_value[i] + tolerance[i]) wait_str = "WAIT: #{_upcase(target_name, packet_name, item_name)}[#{i}]" - range_str = "range #{range.first} to #{range.last} with value == #{value[i]} after waiting #{round(time, 3)} seconds" + range_str = "range #{range.first} to #{range.last} with value == #{value[i]} after waiting #{time} seconds" if range.include?(value[i]) message << "#{wait_str} was within #{range_str}\n" else @@ -263,7 +263,7 @@ def wait_tolerance(*args, type: :CONVERTED, quiet: false, scope: $openc3_scope, time = Time.now.sys - start_time range = (expected_value - tolerance)..(expected_value + tolerance) wait_str = "WAIT: #{_upcase(target_name, packet_name, item_name)}" - range_str = "range #{range.first} to #{range.last} with value == #{value} after waiting #{round(time, 3)} seconds" + range_str = "range #{range.first} to #{range.last} with value == #{value} after waiting #{time} seconds" if success Logger.info "#{wait_str} was within #{range_str}" unless quiet else @@ -753,7 +753,6 @@ def _openc3_script_wait_implementation(target_name, packet_name, item_name, valu end else begin - puts "value:#{value} eval:#{exp_to_eval}" if eval(exp_to_eval) return true, value end From 8d68848f2a4d4debdb3f0901f47cec767a2ac6bd Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Fri, 20 Oct 2023 21:57:17 -0600 Subject: [PATCH 7/7] Update comments --- openc3/lib/openc3/script/api_shared.rb | 2 +- openc3/spec/script/api_shared_spec.rb | 2 +- playwright/tests/admin/plugins.spec.ts | 196 +++++++++--------- playwright/tests/admin/settings.spec.ts | 2 +- playwright/tests/calendar.spec.ts | 4 +- .../tests/cmd-tlm-server/cmd-pkts.spec.ts | 2 +- .../tests/cmd-tlm-server/file-menu.spec.ts | 2 +- .../tests/cmd-tlm-server/interfaces.spec.ts | 12 +- .../tests/cmd-tlm-server/targets.spec.ts | 14 +- .../tests/cmd-tlm-server/tlm-pkts.spec.ts | 12 +- playwright/tests/command-sender.spec.ts | 2 +- playwright/tests/data-extractor.spec.ts | 2 +- playwright/tests/data-viewer.spec.ts | 3 +- playwright/tests/fixture.ts | 12 +- playwright/tests/limits-monitor.spec.ts | 3 +- playwright/tests/packet-viewer.spec.ts | 16 +- playwright/tests/script-runner/api.spec.ts | 18 +- playwright/tests/script-runner/debug.spec.ts | 36 ++-- .../tests/script-runner/edit-menu.spec.ts | 2 +- .../tests/script-runner/file-menu.spec.ts | 18 +- .../tests/script-runner/prompts.spec.ts | 44 ++-- .../tests/script-runner/script-menu.spec.ts | 2 +- playwright/tests/script-runner/suite.spec.ts | 40 ++-- playwright/tests/table-manager.spec.ts | 2 +- playwright/tests/tlm-grapher.spec.ts | 2 +- playwright/tests/tlm-viewer.spec.ts | 48 ++--- playwright/tests/wait-for-build.spec.ts | 11 +- 27 files changed, 255 insertions(+), 254 deletions(-) diff --git a/openc3/lib/openc3/script/api_shared.rb b/openc3/lib/openc3/script/api_shared.rb index 6d6930bec6..07379e1ecd 100644 --- a/openc3/lib/openc3/script/api_shared.rb +++ b/openc3/lib/openc3/script/api_shared.rb @@ -14,7 +14,7 @@ # GNU Affero General Public License for more details. # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved # # This file may also be used under the terms of a commercial license diff --git a/openc3/spec/script/api_shared_spec.rb b/openc3/spec/script/api_shared_spec.rb index 49c188f2c6..f260e97eb1 100644 --- a/openc3/spec/script/api_shared_spec.rb +++ b/openc3/spec/script/api_shared_spec.rb @@ -14,7 +14,7 @@ # GNU Affero General Public License for more details. # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved # # This file may also be used under the terms of a commercial license diff --git a/playwright/tests/admin/plugins.spec.ts b/playwright/tests/admin/plugins.spec.ts index cb35a4b009..c7b22cb696 100644 --- a/playwright/tests/admin/plugins.spec.ts +++ b/playwright/tests/admin/plugins.spec.ts @@ -1,5 +1,5 @@ /* -# Copyright 2022 OpenC3, Inc. +# Copyright 2023 OpenC3, Inc. # All Rights Reserved. # # This program is free software; you can modify and/or redistribute it @@ -25,120 +25,120 @@ test.use({ test('shows and hides built-in tools', async ({ page, utils }) => { await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-demo' + 'openc3-cosmos-demo', ) if (process.env.ENTERPRISE === '1') { await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-enterprise-tool-admin' + 'openc3-cosmos-enterprise-tool-admin', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-enterprise-tool-base' + 'openc3-enterprise-tool-base', ) } else { await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-admin' + 'openc3-cosmos-tool-admin', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-tool-base' + 'openc3-tool-base', ) } await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-autonomic' + 'openc3-cosmos-tool-autonomic', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-calendar' + 'openc3-cosmos-tool-calendar', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-cmdsender' + 'openc3-cosmos-tool-cmdsender', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-cmdtlmserver' + 'openc3-cosmos-tool-cmdtlmserver', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-dataextractor' + 'openc3-cosmos-tool-dataextractor', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-dataviewer' + 'openc3-cosmos-tool-dataviewer', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-handbooks' + 'openc3-cosmos-tool-handbooks', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-limitsmonitor' + 'openc3-cosmos-tool-limitsmonitor', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-packetviewer' + 'openc3-cosmos-tool-packetviewer', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-scriptrunner' + 'openc3-cosmos-tool-scriptrunner', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-tablemanager' + 'openc3-cosmos-tool-tablemanager', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-tlmgrapher' + 'openc3-cosmos-tool-tlmgrapher', ) await expect(page.locator('id=openc3-tool')).not.toContainText( - 'openc3-cosmos-tool-tlmviewer' + 'openc3-cosmos-tool-tlmviewer', ) await page.locator('text=Show Default Tools').click() await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-demo' + 'openc3-cosmos-demo', ) if (process.env.ENTERPRISE === '1') { await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-enterprise-tool-admin' + 'openc3-cosmos-enterprise-tool-admin', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-enterprise-tool-base' + 'openc3-enterprise-tool-base', ) } else { await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-admin' + 'openc3-cosmos-tool-admin', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-tool-base' + 'openc3-tool-base', ) } await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-autonomic' + 'openc3-cosmos-tool-autonomic', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-calendar' + 'openc3-cosmos-tool-calendar', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-cmdsender' + 'openc3-cosmos-tool-cmdsender', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-cmdtlmserver' + 'openc3-cosmos-tool-cmdtlmserver', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-dataextractor' + 'openc3-cosmos-tool-dataextractor', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-dataviewer' + 'openc3-cosmos-tool-dataviewer', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-handbooks' + 'openc3-cosmos-tool-handbooks', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-limitsmonitor' + 'openc3-cosmos-tool-limitsmonitor', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-packetviewer' + 'openc3-cosmos-tool-packetviewer', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-scriptrunner' + 'openc3-cosmos-tool-scriptrunner', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-tablemanager' + 'openc3-cosmos-tool-tablemanager', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-tlmgrapher' + 'openc3-cosmos-tool-tlmgrapher', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'openc3-cosmos-tool-tlmviewer' + 'openc3-cosmos-tool-tlmviewer', ) }) @@ -146,28 +146,28 @@ test('shows targets associated with plugins', async ({ page, utils }) => { // Check that the openc3-demo contains the following targets: await expect( page.locator( - '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")' - ) + '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")', + ), ).toContainText('EXAMPLE') await expect( page.locator( - '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")' - ) + '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")', + ), ).toContainText('INST') await expect( page.locator( - '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")' - ) + '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")', + ), ).toContainText('INST2') await expect( page.locator( - '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")' - ) + '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")', + ), ).toContainText('SYSTEM') await expect( page.locator( - '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")' - ) + '[data-test=plugin-list] div[role=listitem]:has-text("openc3-cosmos-demo")', + ), ).toContainText('TEMPLATED') }) @@ -192,18 +192,18 @@ test('installs a new plugin', async ({ page, utils }) => { await expect(page.locator('.v-dialog:has-text("Variables")')).toBeVisible() await page.locator('data-test=edit-submit').click() await expect(page.locator('[data-test=plugin-alert]')).toContainText( - 'Started installing' + 'Started installing', ) // Plugin install can go so fast we can't count on 'Running' to be present so try catch this let regexp = new RegExp( - `Processing plugin_install: ${pluginGem}__.* - Running` + `Processing plugin_install: ${pluginGem}__.* - Running`, ) try { await expect(page.locator('[data-test=process-list]')).toContainText( regexp, { timeout: 30000, - } + }, ) } catch {} // Ensure no Running are left @@ -211,7 +211,7 @@ test('installs a new plugin', async ({ page, utils }) => { regexp, { timeout: 30000, - } + }, ) // Check for Complete regexp = new RegExp(`Processing plugin_install: ${pluginGem} - Complete`) @@ -219,21 +219,21 @@ test('installs a new plugin', async ({ page, utils }) => { await expect( page.locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}")` - ) + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}")`, + ), ).toContainText('PW_TEST') // Show the process output await page .locator( - `[data-test=process-list] div[role=listitem]:has-text("${plugin}") >> [data-test=show-output]` + `[data-test=process-list] div[role=listitem]:has-text("${plugin}") >> [data-test=show-output]`, ) .first() .click() await expect(page.locator('.v-dialog--active')).toContainText( - 'Process Output' + 'Process Output', ) await expect(page.locator('.v-dialog--active')).toContainText( - `Loading new plugin: ${pluginGem}` + `Loading new plugin: ${pluginGem}`, ) await page.locator('.v-dialog--active >> button:has-text("Ok")').click() }) @@ -246,8 +246,8 @@ test.describe(() => { // Check that there are no links (a) under the current plugin (no modified files) await expect( await page.locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a` - ) + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a`, + ), ).toHaveCount(0) // Create a new script @@ -260,7 +260,7 @@ test.describe(() => { await page.locator('text=File Save As') await page .locator( - '.v-dialog >> .v-treeview-node__root:has-text("PW_TEST") > button' + '.v-dialog >> .v-treeview-node__root:has-text("PW_TEST") > button', ) .click() await page.locator('text=procedures').click() @@ -268,7 +268,7 @@ test.describe(() => { await page.type('[data-test=file-open-save-filename]', '/save_new.rb') await page.locator('[data-test=file-open-save-submit-btn]').click() await expect(page.locator('#sr-controls')).toContainText( - 'PW_TEST/procedures/save_new.rb' + 'PW_TEST/procedures/save_new.rb', ) // Create a new screen @@ -280,12 +280,12 @@ test.describe(() => { await utils.sleep(500) await page.locator('[data-test=new-screen]').click() await expect( - page.locator(`.v-system-bar:has-text("New Screen")`) + page.locator(`.v-system-bar:has-text("New Screen")`), ).toBeVisible() await page.locator('[data-test=new-screen-name]').fill('NEW_SCREEN') await page.locator('button:has-text("Ok")').click() await expect( - page.locator(`.v-system-bar:has-text("PW_TEST NEW_SCREEN")`) + page.locator(`.v-system-bar:has-text("PW_TEST NEW_SCREEN")`), ).toBeVisible() // Download the changes @@ -296,8 +296,8 @@ test.describe(() => { // Check that we have a link to click await expect( await page.locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a` - ) + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a`, + ), ).toHaveCount(1) const [download1] = await Promise.all([ @@ -306,7 +306,7 @@ test.describe(() => { // Download the modified plugin page .locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a` + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a`, ) .click(), ]) @@ -380,7 +380,7 @@ test('upgrades existing plugin', async ({ page, utils }) => { // Opens the file chooser. await page .locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=upgrade-plugin]` + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=upgrade-plugin]`, ) .click(), ]) @@ -392,18 +392,18 @@ test('upgrades existing plugin', async ({ page, utils }) => { await page.locator('text=DELETE MODIFIED').click() await page.locator('data-test=modified-plugin-submit').click() await expect(page.locator('[data-test=plugin-alert]')).toContainText( - 'Started installing' + 'Started installing', ) // Plugin install can go so fast we can't count on 'Running' to be present so try catch this let regexp = new RegExp( - `Processing plugin_install: ${pluginGem}__.* - Running` + `Processing plugin_install: ${pluginGem}__.* - Running`, ) try { await expect(page.locator('[data-test=process-list]')).toContainText( regexp, { timeout: 30000, - } + }, ) } catch {} // Ensure no Running are left @@ -411,7 +411,7 @@ test('upgrades existing plugin', async ({ page, utils }) => { regexp, { timeout: 30000, - } + }, ) // Check for Complete regexp = new RegExp(`Processing plugin_install: ${pluginGem1} - Complete`) @@ -420,8 +420,8 @@ test('upgrades existing plugin', async ({ page, utils }) => { // Check that there are no longer any links (modified targets) await expect( await page.locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a` - ) + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> a`, + ), ).toHaveCount(0) }) @@ -429,40 +429,40 @@ test('edits existing plugin', async ({ page, utils }) => { // Edit then cancel await page .locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=edit-plugin]` + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=edit-plugin]`, ) .click() await expect(page.locator('.v-dialog:has-text("Variables")')).toBeVisible() await page.locator('data-test=edit-cancel').click() await expect( - page.locator('.v-dialog:has-text("Variables")') + page.locator('.v-dialog:has-text("Variables")'), ).not.toBeVisible() // Edit and change a target name (forces re-install) await page .locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=edit-plugin]` + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=edit-plugin]`, ) .click() await expect(page.locator('.v-dialog:has-text("Variables")')).toBeVisible() await page .locator( - '.v-dialog:has-text("Variables") .v-input:has-text("pw_test_target_name") >> input' + '.v-dialog:has-text("Variables") .v-input:has-text("pw_test_target_name") >> input', ) .fill('NEW_TGT') await page.locator('data-test=edit-submit').click() await expect(page.locator('[data-test=plugin-alert]')).toContainText( - 'Started installing' + 'Started installing', ) // Plugin install can go so fast we can't count on 'Running' to be present so try catch this let regexp = new RegExp( - `Processing plugin_install: ${pluginGem}__.* - Running` + `Processing plugin_install: ${pluginGem}__.* - Running`, ) try { await expect(page.locator('[data-test=process-list]')).toContainText( regexp, { timeout: 30000, - } + }, ) } catch {} // Ensure no Running are left @@ -470,7 +470,7 @@ test('edits existing plugin', async ({ page, utils }) => { regexp, { timeout: 30000, - } + }, ) // Check for Complete ... note new installs append '__' regexp = new RegExp(`Processing plugin_install: ${pluginGem1}__.* - Complete`) @@ -478,23 +478,23 @@ test('edits existing plugin', async ({ page, utils }) => { // Ensure the target list is updated to show the new name await expect( page.locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}")` - ) + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}")`, + ), ).not.toContainText('PW_TEST') await expect( page.locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}")` - ) + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}")`, + ), ).toContainText('NEW_TGT') // Show the process output await page .locator( - `[data-test=process-list] div[role=listitem]:has-text("${plugin}") >> [data-test=show-output]` + `[data-test=process-list] div[role=listitem]:has-text("${plugin}") >> [data-test=show-output]`, ) .first() .click() await expect(page.locator('.v-dialog--active')).toContainText( - 'Process Output' + 'Process Output', ) // TODO: Should this be Loading new or Updating existing? // await expect(page.locator('.v-dialog--active')).toContainText('Updating existing plugin') @@ -515,12 +515,12 @@ test.describe(() => { await utils.sleep(500) await page.locator('[data-test=new-screen]').click() await expect( - page.locator(`.v-system-bar:has-text("New Screen")`) + page.locator(`.v-system-bar:has-text("New Screen")`), ).toBeVisible() await page.locator('[data-test=new-screen-name]').fill('NEW_SCREEN') await page.locator('button:has-text("Ok")').click() await expect( - page.locator(`.v-system-bar:has-text("NEW_TGT NEW_SCREEN")`) + page.locator(`.v-system-bar:has-text("NEW_TGT NEW_SCREEN")`), ).toBeVisible() }) }) @@ -528,7 +528,7 @@ test.describe(() => { test('deletes a plugin', async ({ page, utils }) => { await page .locator( - `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=delete-plugin]` + `[data-test=plugin-list] div[role=listitem]:has-text("${plugin}") >> [data-test=delete-plugin]`, ) .click() await expect(page.locator('.v-dialog--active')).toContainText('Confirm') @@ -539,18 +539,18 @@ test('deletes a plugin', async ({ page, utils }) => { await page.locator('data-test=modified-plugin-submit').click() await expect(page.locator('[data-test=plugin-alert]')).toContainText( - 'Removing plugin' + 'Removing plugin', ) // Plugin uninstall can go so fast we can't count on 'Running' to be present so try catch this let regexp = new RegExp( - `Processing plugin_install: ${pluginGem1}__.* - Running` + `Processing plugin_install: ${pluginGem1}__.* - Running`, ) try { await expect(page.locator('[data-test=process-list]')).toContainText( regexp, { timeout: 30000, - } + }, ) } catch {} // Ensure no Running are left @@ -558,28 +558,28 @@ test('deletes a plugin', async ({ page, utils }) => { regexp, { timeout: 30000, - } + }, ) // Check for Complete ... note new installs append '__' regexp = new RegExp( - `Processing plugin_uninstall: ${pluginGem1}__.* - Complete` + `Processing plugin_uninstall: ${pluginGem1}__.* - Complete`, ) await expect(page.locator('[data-test=process-list]')).toContainText(regexp) await expect(page.locator(`[data-test=plugin-list]`)).not.toContainText( - plugin + plugin, ) // Show the process output await page .locator( - `[data-test=process-list] div[role=listitem]:has-text("plugin_uninstall") >> [data-test=show-output]` + `[data-test=process-list] div[role=listitem]:has-text("plugin_uninstall") >> [data-test=show-output]`, ) .first() .click() await expect(page.locator('.v-dialog--active')).toContainText( - 'Process Output' + 'Process Output', ) await expect(page.locator('.v-dialog--active')).toContainText( - 'PluginModel destroyed' + 'PluginModel destroyed', ) await page.locator('.v-dialog--active >> button:has-text("Ok")').click() }) diff --git a/playwright/tests/admin/settings.spec.ts b/playwright/tests/admin/settings.spec.ts index 244049c248..85827f121e 100644 --- a/playwright/tests/admin/settings.spec.ts +++ b/playwright/tests/admin/settings.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/calendar.spec.ts b/playwright/tests/calendar.spec.ts index 48497c4f02..e8a019549c 100644 --- a/playwright/tests/calendar.spec.ts +++ b/playwright/tests/calendar.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -52,7 +52,7 @@ test('file menu', async ({ page, utils }) => { '[data-test=cosmos-calendar-file-download-event-list]', function (contents) { expect(contents).toContain('') // % is empty - } + }, ) }) diff --git a/playwright/tests/cmd-tlm-server/cmd-pkts.spec.ts b/playwright/tests/cmd-tlm-server/cmd-pkts.spec.ts index 888a343499..9045a25f7d 100644 --- a/playwright/tests/cmd-tlm-server/cmd-pkts.spec.ts +++ b/playwright/tests/cmd-tlm-server/cmd-pkts.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/cmd-tlm-server/file-menu.spec.ts b/playwright/tests/cmd-tlm-server/file-menu.spec.ts index 3bfb5d1ecb..211540eed3 100644 --- a/playwright/tests/cmd-tlm-server/file-menu.spec.ts +++ b/playwright/tests/cmd-tlm-server/file-menu.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/cmd-tlm-server/interfaces.spec.ts b/playwright/tests/cmd-tlm-server/interfaces.spec.ts index 7caad57c42..af7c8a3262 100644 --- a/playwright/tests/cmd-tlm-server/interfaces.spec.ts +++ b/playwright/tests/cmd-tlm-server/interfaces.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -27,20 +27,20 @@ test.use({ test('disconnects and connects an interface', async ({ page, utils }) => { await expect( - page.locator('tr:has-text("INST_INT") td >> nth=2') + page.locator('tr:has-text("INST_INT") td >> nth=2'), ).toContainText('CONNECTED') await page.locator('tr:has-text("INST_INT") td >> nth=1').click() await expect( - page.locator('tr:has-text("INST_INT") td >> nth=2') + page.locator('tr:has-text("INST_INT") td >> nth=2'), ).toContainText('DISCONNECTED') await expect(page.locator('[data-test=log-messages]')).toContainText( - 'INST_INT: Disconnect' + 'INST_INT: Disconnect', ) await page.locator('tr:has-text("INST_INT") td >> nth=1').click() await expect( - page.locator('tr:has-text("INST_INT") td >> nth=2') + page.locator('tr:has-text("INST_INT") td >> nth=2'), ).toContainText('CONNECTED') await expect(page.locator('[data-test=log-messages]')).toContainText( - 'INST_INT: Connection Success' + 'INST_INT: Connection Success', ) }) diff --git a/playwright/tests/cmd-tlm-server/targets.spec.ts b/playwright/tests/cmd-tlm-server/targets.spec.ts index adc65dd565..6386f96578 100644 --- a/playwright/tests/cmd-tlm-server/targets.spec.ts +++ b/playwright/tests/cmd-tlm-server/targets.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -29,24 +29,24 @@ test('displays the list of targets', async ({ page, utils }) => { await expect(page.locator('[data-test=targets-table]')).toContainText('INST') await expect(page.locator('[data-test=targets-table]')).toContainText('INST2') await expect(page.locator('[data-test=targets-table]')).toContainText( - 'EXAMPLE' + 'EXAMPLE', ) await expect(page.locator('[data-test=targets-table]')).toContainText( - 'TEMPLATED' + 'TEMPLATED', ) }) test('displays the interfaces', async ({ page, utils }) => { await expect(page.locator('[data-test=targets-table]')).toContainText( - 'INST_INT' + 'INST_INT', ) await expect(page.locator('[data-test=targets-table]')).toContainText( - 'INST2_INT' + 'INST2_INT', ) await expect(page.locator('[data-test=targets-table]')).toContainText( - 'EXAMPLE_INT' + 'EXAMPLE_INT', ) await expect(page.locator('[data-test=targets-table]')).toContainText( - 'TEMPLATED_INT' + 'TEMPLATED_INT', ) }) diff --git a/playwright/tests/cmd-tlm-server/tlm-pkts.spec.ts b/playwright/tests/cmd-tlm-server/tlm-pkts.spec.ts index fedff70aef..aafe11fe9e 100644 --- a/playwright/tests/cmd-tlm-server/tlm-pkts.spec.ts +++ b/playwright/tests/cmd-tlm-server/tlm-pkts.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -37,11 +37,11 @@ test('displays the packet count', async ({ page, utils }) => { await utils.sleep(2000) // Allow the telemetry to be fetched expect( parseInt( - await page.locator('text=INSTHEALTH_STATUS >> td >> nth=2').textContent() - ) + await page.locator('text=INSTHEALTH_STATUS >> td >> nth=2').textContent(), + ), ).toBeGreaterThan(50) expect( - parseInt(await page.locator('text=INSTADCS >> td >> nth=2').textContent()) + parseInt(await page.locator('text=INSTADCS >> td >> nth=2').textContent()), ).toBeGreaterThan(500) }) @@ -52,7 +52,7 @@ test('displays a raw packet', async ({ page, utils }) => { .getByRole('button', { name: 'View Raw' }) .click() await expect(page.locator('.v-dialog')).toContainText( - 'Raw Telemetry Packet: INST HEALTH_STATUS' + 'Raw Telemetry Packet: INST HEALTH_STATUS', ) await expect(page.locator('.v-dialog')).toContainText('Received Time:') await expect(page.locator('.v-dialog')).toContainText('Count:') @@ -80,6 +80,6 @@ test('links to packet viewer', async ({ page, utils }) => { timeout: 30000, }) await expect(newPage.locator('id=openc3-tool')).toContainText( - 'Health and status from the INST target' + 'Health and status from the INST target', ) }) diff --git a/playwright/tests/command-sender.spec.ts b/playwright/tests/command-sender.spec.ts index 21bb8c661e..7e85dc9acc 100644 --- a/playwright/tests/command-sender.spec.ts +++ b/playwright/tests/command-sender.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/data-extractor.spec.ts b/playwright/tests/data-extractor.spec.ts index 5b18869a71..06cd081a91 100644 --- a/playwright/tests/data-extractor.spec.ts +++ b/playwright/tests/data-extractor.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/data-viewer.spec.ts b/playwright/tests/data-viewer.spec.ts index 09a7a2e050..5cafa71c26 100644 --- a/playwright/tests/data-viewer.spec.ts +++ b/playwright/tests/data-viewer.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -74,7 +74,6 @@ test('saves the configuration', async ({ page, utils }) => { }) test('opens and resets the configuration', async ({ page, utils }) => { - test.slow() // Open the config await page.locator('[data-test="cosmos-data-viewer-file"]').click() await page.locator('text=Open Configuration').click() diff --git a/playwright/tests/fixture.ts b/playwright/tests/fixture.ts index 7f80f7dd5d..fd8d1d0a81 100644 --- a/playwright/tests/fixture.ts +++ b/playwright/tests/fixture.ts @@ -1,5 +1,5 @@ /* -# Copyright 2022 OpenC3, Inc. +# Copyright 2023 OpenC3, Inc. # All Rights Reserved. # # This program is free software; you can modify and/or redistribute it @@ -101,8 +101,8 @@ export const test = base.extend<{ // Copyright (c) 2021 Anish Karandikar await context.addInitScript(() => window.addEventListener('beforeunload', () => - window.collectIstanbulCoverage(JSON.stringify(window.__coverage__)) - ) + window.collectIstanbulCoverage(JSON.stringify(window.__coverage__)), + ), ) await fs.promises.mkdir(istanbulTempDir, { recursive: true }) await context.exposeFunction('collectIstanbulCoverage', (coverageJSON) => { @@ -110,9 +110,9 @@ export const test = base.extend<{ fs.writeFileSync( path.join( istanbulTempDir, - `playwright_coverage_${generateUUID()}.json` + `playwright_coverage_${generateUUID()}.json`, ), - coverageJSON + coverageJSON, ) }) // End Copyright @@ -124,7 +124,7 @@ export const test = base.extend<{ // Copyright (c) 2021 Anish Karandikar for (const page of context.pages()) { await page.evaluate(() => - window.collectIstanbulCoverage(JSON.stringify(window.__coverage__)) + window.collectIstanbulCoverage(JSON.stringify(window.__coverage__)), ) } // End Copyright diff --git a/playwright/tests/limits-monitor.spec.ts b/playwright/tests/limits-monitor.spec.ts index f4b6b82795..59656f323e 100644 --- a/playwright/tests/limits-monitor.spec.ts +++ b/playwright/tests/limits-monitor.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -87,7 +87,6 @@ test('saves the configuration', async ({ page, utils }) => { }) test('opens and resets the configuration', async ({ page, utils }) => { - test.slow() await page.locator('[data-test=cosmos-limits-monitor-file]').click() await page.locator('text=Open Configuration').click() await page.locator(`td:has-text("playwright")`).click() diff --git a/playwright/tests/packet-viewer.spec.ts b/playwright/tests/packet-viewer.spec.ts index 30064f82ac..dbcce14a9f 100644 --- a/playwright/tests/packet-viewer.spec.ts +++ b/playwright/tests/packet-viewer.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -45,10 +45,10 @@ test('displays INST HEALTH_STATUS and polls the api', async ({ await utils.inputValue( page, '[data-test=select-packet] input', - 'HEALTH_STATUS' + 'HEALTH_STATUS', ) await expect(page.locator('id=openc3-tool')).toContainText( - 'Health and status' + 'Health and status', ) // Description page.on('request', (request) => { @@ -65,7 +65,7 @@ test('selects a target and packet to display', async ({ page, utils }) => { await utils.inputValue(page, '[data-test=select-target] input', 'INST') await utils.inputValue(page, '[data-test=select-packet] input', 'IMAGE') await expect(page.locator('id=openc3-tool')).toContainText( - 'Packet with image data' + 'Packet with image data', ) await expect(page.locator('id=openc3-tool')).toContainText('BYTES') }) @@ -78,12 +78,12 @@ test('gets details with right click', async ({ page, utils }) => { await page.getByRole('menuitem', { name: 'Details' }).click() await expect(page.locator('.v-dialog--active')).toBeVisible() await expect(page.locator('.v-dialog--active')).toContainText( - 'INST HEALTH_STATUS TEMP2' + 'INST HEALTH_STATUS TEMP2', ) // Check that a few of the details are there ... that proves the API request await expect(page.locator('.v-dialog--active')).toContainText('FLOAT') await expect(page.locator('.v-dialog--active')).toContainText( - 'PolynomialConversion' + 'PolynomialConversion', ) await expect(page.locator('.v-dialog--active')).toContainText('CELSIUS') @@ -97,12 +97,12 @@ test('gets details with right click', async ({ page, utils }) => { await page.getByRole('menuitem', { name: 'Details' }).click() await expect(page.locator('.v-dialog--active')).toBeVisible() await expect(page.locator('.v-dialog--active')).toContainText( - 'INST HEALTH_STATUS PACKET_TIMESECONDS' + 'INST HEALTH_STATUS PACKET_TIMESECONDS', ) // Check that a few of the details are there ... that proves the API request await expect(page.locator('.v-dialog--active')).toContainText('DERIVED') await expect(page.locator('.v-dialog--active')).toContainText( - 'PacketTimeSecondsConversion' + 'PacketTimeSecondsConversion', ) }) diff --git a/playwright/tests/script-runner/api.spec.ts b/playwright/tests/script-runner/api.spec.ts index 27fa9cb737..446f4db58c 100644 --- a/playwright/tests/script-runner/api.spec.ts +++ b/playwright/tests/script-runner/api.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -54,30 +54,30 @@ test('opens a target file', async ({ page, utils }) => { timeout: 30000, }) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'Writing DEFAULT/targets_modified/INST/test.txt' + 'Writing DEFAULT/targets_modified/INST/test.txt', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'Reading DEFAULT/targets_modified/INST/test.txt' + 'Reading DEFAULT/targets_modified/INST/test.txt', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'file contents' + 'file contents', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'Deleting DEFAULT/targets_modified/INST/test.txt' + 'Deleting DEFAULT/targets_modified/INST/test.txt', ) // Restart after the error await page.locator('[data-test=go-button]').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'Reading DEFAULT/targets_modified/INST/screens/web.txt' + 'Reading DEFAULT/targets_modified/INST/screens/web.txt', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'Edited web' + 'Edited web', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'Reading DEFAULT/targets/INST/screens/web.txt' + 'Reading DEFAULT/targets/INST/screens/web.txt', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'Original web' + 'Original web', ) await expect(page.locator('[data-test=state]')).toHaveValue('stopped') }) diff --git a/playwright/tests/script-runner/debug.spec.ts b/playwright/tests/script-runner/debug.spec.ts index 435057af20..83f73630c3 100644 --- a/playwright/tests/script-runner/debug.spec.ts +++ b/playwright/tests/script-runner/debug.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -43,12 +43,12 @@ test('keeps a debug command history', async ({ page, utils }) => { await page.locator('[data-test=debug-text]').type('x') await page.keyboard.press('Enter') await expect(page.locator('[data-test=output-messages]')).toContainText( - '12345' + '12345', ) await page.locator('[data-test=debug-text]').type('puts "abc123!"') await page.keyboard.press('Enter') await expect(page.locator('[data-test=output-messages]')).toContainText( - 'abc123!' + 'abc123!', ) await page.locator('[data-test=debug-text]').type('x = 67890') await page.keyboard.press('Enter') @@ -58,7 +58,7 @@ test('keeps a debug command history', async ({ page, utils }) => { expect(await page.inputValue('[data-test=debug-text]')).toMatch('x = 67890') await page.keyboard.press('ArrowUp') expect(await page.inputValue('[data-test=debug-text]')).toMatch( - 'puts "abc123!"' + 'puts "abc123!"', ) await page.keyboard.press('ArrowUp') expect(await page.inputValue('[data-test=debug-text]')).toMatch('x') @@ -68,7 +68,7 @@ test('keeps a debug command history', async ({ page, utils }) => { expect(await page.inputValue('[data-test=debug-text]')).toMatch('x') await page.keyboard.press('ArrowDown') expect(await page.inputValue('[data-test=debug-text]')).toMatch( - 'puts "abc123!"' + 'puts "abc123!"', ) await page.keyboard.press('ArrowDown') expect(await page.inputValue('[data-test=debug-text]')).toMatch('x = 67890') @@ -86,7 +86,7 @@ test('keeps a debug command history', async ({ page, utils }) => { await expect(page.locator('[data-test=state]')).toHaveValue('stopped') // Verify we were able to change the 'x' variable await expect(page.locator('[data-test=output-messages]')).toContainText( - 'x:67890' + 'x:67890', ) await page.locator('[data-test=cosmos-script-runner-script]').click() @@ -102,12 +102,12 @@ test('retries failed checks', async ({ page, utils }) => { }) // Check for the initial check message await expect(page.locator('[data-test=output-messages]')).toContainText( - '1 == 2 is FALSE' + '1 == 2 is FALSE', ) await page.locator('[data-test=pause-retry-button]').click() // Retry // Now we should have two error messages await expect( - page.locator('[data-test=output-messages] td:has-text("1 == 2 is FALSE")') + page.locator('[data-test=output-messages] td:has-text("1 == 2 is FALSE")'), ).toHaveCount(2) await expect(page.locator('[data-test=state]')).toHaveValue('error') await page.locator('[data-test=go-button]').click() @@ -125,7 +125,7 @@ test('displays the call stack', async ({ page, utils }) => { // page.locator('[data-test=cosmos-script-runner-script-call-stack]') // ).toBeDisabled() await expect( - page.locator('[data-test=cosmos-script-runner-script-call-stack]') + page.locator('[data-test=cosmos-script-runner-script-call-stack]'), ).toHaveAttribute('aria-disabled', 'true') // await expect(page.locator('[data-test=cosmos-script-runner-script-call-stack]')).toBeDisabled() @@ -154,7 +154,7 @@ test('displays the call stack', async ({ page, utils }) => { await page.locator('[data-test=cosmos-script-runner-script]').click() await expect( - page.locator('[data-test=cosmos-script-runner-script-call-stack]') + page.locator('[data-test=cosmos-script-runner-script-call-stack]'), ).toHaveAttribute('aria-disabled', 'true') }) @@ -187,10 +187,10 @@ test('displays disconnect icon', async ({ page, utils }) => { timeout: 20000, }) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'total:0' // collect count does not change + 'total:0', // collect count does not change ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'disconnect:100' + 'disconnect:100', ) await page.locator('[data-test=cosmos-script-runner-script]').click() @@ -234,7 +234,7 @@ test('remembers breakpoints and clears all', async ({ page, utils }) => { await page.locator('text=checks >> nth=0').click() // nth=0 because INST, INST2 await page.locator('[data-test=file-open-save-submit-btn]').click() expect(await page.locator('#sr-controls')).toContainText( - `INST/procedures/checks.rb` + `INST/procedures/checks.rb`, ) await utils.sleep(1000) // Clicking on the ace gutters requires a little wait await page.locator('.ace_gutter-cell').nth(1).click({ force: true }) @@ -246,14 +246,14 @@ test('remembers breakpoints and clears all', async ({ page, utils }) => { await utils.sleep(1000) // allow page to reload // Reloading the page should bring up the previous script expect(await page.locator('#sr-controls')).toContainText( - `INST/procedures/checks.rb` + `INST/procedures/checks.rb`, ) await expect(page.locator('.ace_gutter-cell').nth(1)).toHaveClass( - 'ace_gutter-cell ace_breakpoint' + 'ace_gutter-cell ace_breakpoint', ) await expect(page.locator('.ace_gutter-cell').nth(3)).toHaveClass( - 'ace_gutter-cell ace_breakpoint' + 'ace_gutter-cell ace_breakpoint', ) await page.locator('[data-test=cosmos-script-runner-script]').click() @@ -261,9 +261,9 @@ test('remembers breakpoints and clears all', async ({ page, utils }) => { await page.locator('.v-dialog >> button:has-text("Delete")').click() await expect(page.locator('.ace_gutter-cell').nth(1)).toHaveClass( - 'ace_gutter-cell ' + 'ace_gutter-cell ', ) await expect(page.locator('.ace_gutter-cell').nth(3)).toHaveClass( - 'ace_gutter-cell ' + 'ace_gutter-cell ', ) }) diff --git a/playwright/tests/script-runner/edit-menu.spec.ts b/playwright/tests/script-runner/edit-menu.spec.ts index 7b6ccd10e5..02a19e09e4 100644 --- a/playwright/tests/script-runner/edit-menu.spec.ts +++ b/playwright/tests/script-runner/edit-menu.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/script-runner/file-menu.spec.ts b/playwright/tests/script-runner/file-menu.spec.ts index c91b525adb..2444317fa9 100644 --- a/playwright/tests/script-runner/file-menu.spec.ts +++ b/playwright/tests/script-runner/file-menu.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -50,14 +50,14 @@ test('open a file', async ({ page, utils }) => { await page.locator('[data-test=file-open-save-submit-btn]').click() await expect(page.locator('.v-dialog')).not.toBeVisible() expect(await page.locator('#sr-controls')).toContainText( - `INST/procedures/disconnect.rb` + `INST/procedures/disconnect.rb`, ) // Reload and verify the file is still there await page.reload() await utils.sleep(1000) // allow page to reload expect(await page.locator('#sr-controls')).toContainText( - `INST/procedures/disconnect.rb` + `INST/procedures/disconnect.rb`, ) }) @@ -71,7 +71,7 @@ test('handles File->Save new file', async ({ page, utils }) => { await page.locator('text=temp.rb is not a valid filename') await page .locator( - '.v-dialog >> .v-treeview-node__root:has-text("INST") > button >> nth=0' + '.v-dialog >> .v-treeview-node__root:has-text("INST") > button >> nth=0', ) .click() await page.getByText('procedures', { exact: true }).click() @@ -79,7 +79,7 @@ test('handles File->Save new file', async ({ page, utils }) => { await page.type('[data-test=file-open-save-filename]', '/save_new.rb') await page.locator('[data-test=file-open-save-submit-btn]').click() expect(await page.locator('#sr-controls')).toContainText( - 'INST/procedures/save_new.rb' + 'INST/procedures/save_new.rb', ) // Delete the file @@ -98,7 +98,7 @@ test('handles File Save overwrite', async ({ page, utils }) => { .fill('INST/procedures/save_overwrite.rb') await page.locator('[data-test=file-open-save-submit-btn]').click() expect(await page.locator('#sr-controls')).toContainText( - 'INST/procedures/save_overwrite.rb' + 'INST/procedures/save_overwrite.rb', ) await page.locator('textarea').fill('# comment1') @@ -141,7 +141,7 @@ test('handles Download', async ({ page, utils }) => { '[data-test=cosmos-script-runner-file-download]', function (contents) { expect(contents).toContain('download this') - } + }, ) // Delete the file @@ -162,7 +162,7 @@ test('can delete all temp files', async ({ page, utils }) => { timeout: 20000, }) await expect(page.locator('#sr-controls')).toContainText( - /__TEMP__\/\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}_\d{3}_temp.rb/ + /__TEMP__\/\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}_\d{3}_temp.rb/, ) // Weird selector but it's how vuetify works let tempFile1 = await page @@ -183,7 +183,7 @@ test('can delete all temp files', async ({ page, utils }) => { timeout: 20000, }) await expect(page.locator('#sr-controls')).toContainText( - /__TEMP__\/\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}_\d{3}_temp.rb/ + /__TEMP__\/\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}_\d{3}_temp.rb/, ) let tempFile2 = await page .locator('#sr-controls >> input[type=hidden]') diff --git a/playwright/tests/script-runner/prompts.spec.ts b/playwright/tests/script-runner/prompts.spec.ts index eb355ba59b..52b044b888 100644 --- a/playwright/tests/script-runner/prompts.spec.ts +++ b/playwright/tests/script-runner/prompts.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -38,10 +38,10 @@ test('prompts for hazardous commands', async ({ page, utils }) => { await page.locator('.v-dialog >> button:has-text("Yes")').click() await expect(page.locator('[data-test=state]')).toHaveValue('stopped') await expect(page.locator('[data-test=output-messages]')).toContainText( - 'User input: Yes' + 'User input: Yes', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'cmd("INST CLEAR")' + 'cmd("INST CLEAR")', ) }) @@ -73,7 +73,7 @@ test('errors for out of range command parameters', async ({ page, utils }) => { await page.locator('[data-test=go-button]').click() await expect(page.locator('[data-test=state]')).toHaveValue('stopped') await expect(page.locator('[data-test=output-messages]')).toContainText( - '11 not in valid range' + '11 not in valid range', ) }) @@ -112,31 +112,31 @@ test('ask accepts default, password, and required', async ({ page, utils }) => { }) await page.locator('.v-dialog >> button:has-text("Cancel")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'User input: Cancel' + 'User input: Cancel', ) await expect(page.locator('[data-test=state]')).toHaveValue('paused') // Clicking go re-launches the dialog await page.locator('[data-test=go-button]').click() await expect( - page.locator('.v-dialog >> button:has-text("Ok")') + page.locator('.v-dialog >> button:has-text("Ok")'), ).toBeDisabled() await page.locator('.v-dialog >> input').type('12345') await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - '12345' + '12345', ) // Now nothing is required so OK is enabled await expect(page.locator('.v-dialog >> button:has-text("Ok")')).toBeEnabled() await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'blank:true' + 'blank:true', ) // Verify the default value expect(await page.inputValue('[data-test=ask-value-input]')).toMatch('67890') await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - '67890' + '67890', ) // Now type the secret password await page.locator('.v-dialog >> input').type('abc123!') @@ -145,13 +145,13 @@ test('ask accepts default, password, and required', async ({ page, utils }) => { await expect(page.locator('[data-test=state]')).toHaveValue('waiting') // Verify we're not outputting the secret password on input await expect(page.locator('[data-test=output-messages]')).not.toContainText( - 'abc123!' + 'abc123!', ) // Once we restart we should see it since we print it await page.locator('[data-test=go-button]').click() await expect(page.locator('[data-test=state]')).toHaveValue('stopped') await expect(page.locator('[data-test=output-messages]')).toContainText( - 'abc123!' + 'abc123!', ) }) @@ -171,17 +171,17 @@ test('converts value for ask but not ask_string', async ({ page, utils }) => { await page.locator('.v-dialog >> input').type('123') await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'int:123 Integer' + 'int:123 Integer', ) await page.locator('.v-dialog >> input').type('5.5') await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'float:5.5 Float' + 'float:5.5 Float', ) await page.locator('.v-dialog >> input').type('5.5') await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'string:5.5 String' + 'string:5.5 String', ) await expect(page.locator('[data-test=state]')).toHaveValue('stopped') }) @@ -202,7 +202,7 @@ test('opens a dialog with buttons for message_box, vertical_message_box', async }) await page.locator('.v-dialog >> button:has-text("Cancel")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'User input: Cancel' + 'User input: Cancel', ) await expect(page.locator('[data-test=state]')).toHaveValue('paused') @@ -213,7 +213,7 @@ test('opens a dialog with buttons for message_box, vertical_message_box', async await expect(page.locator('[data-test=state]')).toHaveValue('stopped') await expect(page.locator('[data-test=output-messages]')).toContainText('TWO') await expect(page.locator('[data-test=output-messages]')).toContainText( - 'FOUR' + 'FOUR', ) }) @@ -228,7 +228,7 @@ test('opens a dialog with dropdowns for combo_box', async ({ page, utils }) => { }) await page.locator('.v-dialog >> button:has-text("Cancel")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'User input: Cancel' + 'User input: Cancel', ) await expect(page.locator('[data-test=state]')).toHaveValue('paused') @@ -240,7 +240,7 @@ test('opens a dialog with dropdowns for combo_box', async ({ page, utils }) => { await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=state]')).toHaveValue('stopped') await expect(page.locator('[data-test=output-messages]')).toContainText( - 'User input: def456' + 'User input: def456', ) }) @@ -257,7 +257,7 @@ test('opens a dialog for prompt', async ({ page, utils }) => { await expect(page.locator('.v-dialog')).toContainText('Continue?') await page.locator('.v-dialog >> button:has-text("Cancel")').click() await expect(page.locator('[data-test=output-messages]')).toContainText( - 'User input: Cancel' + 'User input: Cancel', ) await expect(page.locator('[data-test=state]')).toHaveValue('paused') @@ -282,7 +282,7 @@ test.skip('opens a file dialog', async ({ page, utils }) => { }) await expect(page.locator('.v-dialog')).toContainText('Open a single file') await expect(page.locator('.v-dialog')).toContainText( - 'Choose something interesting' + 'Choose something interesting', ) await page.locator('.v-dialog >> button:has-text("Cancel")').click() await expect(page.locator('[data-test=state]')).toHaveValue('paused') @@ -302,9 +302,9 @@ test.skip('opens a file dialog', async ({ page, utils }) => { await page.locator('.v-dialog >> button:has-text("Ok")').click() await expect(page.locator('[data-test=state]')).toHaveValue('stopped') await expect(page.locator('[data-test=output-messages]')).toContainText( - 'File(s): [".env"]' + 'File(s): [".env"]', ) await expect(page.locator('[data-test=output-messages]')).toContainText( - 'RUBYGEMS_URL' + 'RUBYGEMS_URL', ) }) diff --git a/playwright/tests/script-runner/script-menu.spec.ts b/playwright/tests/script-runner/script-menu.spec.ts index cd92095c58..1ada88d95c 100644 --- a/playwright/tests/script-runner/script-menu.spec.ts +++ b/playwright/tests/script-runner/script-menu.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/script-runner/suite.spec.ts b/playwright/tests/script-runner/suite.spec.ts index 9b9489f81e..51b60c6a86 100644 --- a/playwright/tests/script-runner/suite.spec.ts +++ b/playwright/tests/script-runner/suite.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -43,7 +43,7 @@ async function saveAs(page, filename: string) { await expect(page.locator('[data-test=start-suite]')).toBeVisible() expect(await page.locator('#sr-controls')).toContainText( - `INST/procedures/${filename}` + `INST/procedures/${filename}`, ) } @@ -61,7 +61,7 @@ async function runAndCheckResults( utils, startLocator, validator, - download = false + download = false, ) { await page.locator(startLocator).click() // Wait for the results ... allow for additional time @@ -69,7 +69,7 @@ async function runAndCheckResults( 'Script Results', { timeout: 30000, - } + }, ) // Allow the caller to validate the results validator(await page.inputValue('.v-dialog >> textarea')) @@ -82,7 +82,7 @@ async function runAndCheckResults( function (contents) { expect(contents).toContain('Script Report') validator(contents) - } + }, ) } await page.locator('button:has-text("Ok")').click() @@ -94,11 +94,11 @@ test('generates a suite template', async ({ page, utils }) => { await utils.sleep(1000) // Verify the drop downs are populated await expect( - page.locator('role=button[name="Suite: TestSuite"]') + page.locator('role=button[name="Suite: TestSuite"]'), ).toBeEnabled() await expect(page.locator('role=button[name="Group: Power"]')).toBeEnabled() await expect( - page.locator('role=button[name="Script: script_power_on"]') + page.locator('role=button[name="Script: script_power_on"]'), ).toBeEnabled() // // Verify Suite Start buttons are enabled await expect(page.locator('[data-test=start-suite]')).toBeEnabled() @@ -117,7 +117,7 @@ test('loads Suite controls when opening a suite', async ({ page, utils }) => { await page.locator('text=script_suite >> nth=0').click() // nth=0 because INST, INST2 await page.locator('[data-test=file-open-save-submit-btn]').click() expect(await page.locator('#sr-controls')).toContainText( - `INST/procedures/my_script_suite.rb` + `INST/procedures/my_script_suite.rb`, ) // Verify defaults in the Suite options await expect(page.locator('[data-test=pause-on-error]')).toBeChecked() @@ -129,10 +129,10 @@ test('loads Suite controls when opening a suite', async ({ page, utils }) => { // Verify the drop downs are populated await expect(page.locator('role=button[name="Suite: MySuite"]')).toBeEnabled() await expect( - page.locator('role=button[name="Group: ExampleGroup"]') + page.locator('role=button[name="Group: ExampleGroup"]'), ).toBeEnabled() await expect( - page.locator('role=button[name="Script: script_2"]') + page.locator('role=button[name="Script: script_2"]'), ).toBeEnabled() // // Verify Suite Start buttons are enabled await expect(page.locator('[data-test=start-suite]')).toBeEnabled() @@ -151,7 +151,7 @@ test('loads Suite controls when opening a suite', async ({ page, utils }) => { await page.locator('text=disconnect >> nth=0').click() // nth=0 because INST, INST2 await page.locator('[data-test=file-open-save-submit-btn]').click() expect(await page.locator('#sr-controls')).toContainText( - `INST/procedures/disconnect.rb` + `INST/procedures/disconnect.rb`, ) await expect(page.locator('[data-test=start-suite]')).not.toBeVisible() await expect(page.locator('[data-test=start-group]')).not.toBeVisible() @@ -225,7 +225,7 @@ test('starts a suite', async ({ page, utils }) => { expect(textarea).toMatch('setup:PASS') expect(textarea).toMatch('Total Tests: 1') expect(textarea).toMatch('Pass: 1') - } + }, ) // Run suite teardown @@ -237,7 +237,7 @@ test('starts a suite', async ({ page, utils }) => { expect(textarea).toMatch('teardown:PASS') expect(textarea).toMatch('Total Tests: 1') expect(textarea).toMatch('Pass: 1') - } + }, ) // Run suite @@ -251,7 +251,7 @@ test('starts a suite', async ({ page, utils }) => { expect(textarea).toMatch('Total Tests: 3') expect(textarea).toMatch('Pass: 3') }, - true + true, ) // Rewrite the script but remove setup and teardown @@ -322,7 +322,7 @@ test('starts a group', async ({ page, utils }) => { expect(textarea).toMatch('setup:PASS') expect(textarea).toMatch('Total Tests: 1') expect(textarea).toMatch('Pass: 1') - } + }, ) // Run group teardown @@ -334,7 +334,7 @@ test('starts a group', async ({ page, utils }) => { expect(textarea).toMatch('teardown:PASS') expect(textarea).toMatch('Total Tests: 1') expect(textarea).toMatch('Pass: 1') - } + }, ) // Run group @@ -347,7 +347,7 @@ test('starts a group', async ({ page, utils }) => { expect(textarea).toMatch('teardown:PASS') expect(textarea).toMatch('Total Tests: 3') expect(textarea).toMatch('Pass: 3') - } + }, ) // Rewrite the script but remove setup and teardown @@ -414,7 +414,7 @@ test('starts a script', async ({ page, utils }) => { expect(textarea).toMatch('test1') expect(textarea).toMatch('Total Tests: 1') expect(textarea).toMatch('Pass: 1') - } + }, ) await deleteFile(page) }) @@ -446,7 +446,7 @@ test('handles manual mode', async ({ page, utils }) => { expect(textarea).not.toMatch('manual2') expect(textarea).toMatch('Total Tests: 2') expect(textarea).toMatch('Pass: 2') - } + }, ) await page.locator('label:has-text("Manual")').click() // uncheck Manual // Run group @@ -460,7 +460,7 @@ test('handles manual mode', async ({ page, utils }) => { expect(textarea).toMatch('manual2') expect(textarea).toMatch('Total Tests: 2') expect(textarea).toMatch('Pass: 2') - } + }, ) await deleteFile(page) }) diff --git a/playwright/tests/table-manager.spec.ts b/playwright/tests/table-manager.spec.ts index 136dbd8122..1c3d4f161e 100644 --- a/playwright/tests/table-manager.spec.ts +++ b/playwright/tests/table-manager.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/tlm-grapher.spec.ts b/playwright/tests/tlm-grapher.spec.ts index 42779cf67e..c5dfdcc72d 100644 --- a/playwright/tests/tlm-grapher.spec.ts +++ b/playwright/tests/tlm-grapher.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ diff --git a/playwright/tests/tlm-viewer.spec.ts b/playwright/tests/tlm-viewer.spec.ts index 7e0b9f0088..7ae0348285 100644 --- a/playwright/tests/tlm-viewer.spec.ts +++ b/playwright/tests/tlm-viewer.spec.ts @@ -13,7 +13,7 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ @@ -38,14 +38,14 @@ async function showScreen(page, target, screen, callback = null) { await page.locator('div[role="button"]:has-text("Select Screen")').click() await page.locator(`.v-list-item__title:text-is("${screen}")`).click() await expect( - page.locator(`.v-system-bar:has-text("${target} ${screen}")`) + page.locator(`.v-system-bar:has-text("${target} ${screen}")`), ).toBeVisible() if (callback) { await callback() } await page.locator('[data-test=close-screen-icon]').click() await expect( - page.locator(`.v-system-bar:has-text("${target} ${screen}")`) + page.locator(`.v-system-bar:has-text("${target} ${screen}")`), ).not.toBeVisible() } @@ -83,13 +83,13 @@ test('displays INST COMMANDING', async ({ page, utils }) => { await page.getByRole('button', { name: 'Send' }).click() await expect( - page.getByText('Warning: Command is Hazardous. Send?') + page.getByText('Warning: Command is Hazardous. Send?'), ).toBeVisible() await page.getByRole('button', { name: 'Yes' }).click() // Click on the Run Script: select ... this was generated by playwright codegen await page .locator( - 'div:nth-child(7) > .d-flex > div > .v-input__control > .v-input__slot' + 'div:nth-child(7) > .d-flex > div > .v-input__control > .v-input__slot', ) .first() .click() @@ -138,34 +138,34 @@ test('displays INST LAUNCHER', async ({ page, utils }) => { await page.locator('div[role="button"]:has-text("Select Screen")').click() await page.locator(`.v-list-item__title:text-is("LAUNCHER")`).click() await expect( - page.locator('.v-system-bar:has-text("INST LAUNCHER")') + page.locator('.v-system-bar:has-text("INST LAUNCHER")'), ).toBeVisible() await page.getByRole('button', { name: 'HS', exact: true }).click() await expect(page.locator('.v-system-bar:has-text("INST HS")')).toBeVisible() await page.getByRole('button', { name: 'CMD', exact: true }).click() await expect( - page.locator('.v-system-bar:has-text("INST COMMANDING")') + page.locator('.v-system-bar:has-text("INST COMMANDING")'), ).toBeVisible() await page.getByRole('button', { name: 'GROUND', exact: true }).click() await expect( - page.locator('.v-system-bar:has-text("INST GROUND")') + page.locator('.v-system-bar:has-text("INST GROUND")'), ).toBeVisible() await page.getByRole('button', { name: 'Close HS & CMD' }).click() await expect( - page.locator('.v-system-bar:has-text("INST HS")') + page.locator('.v-system-bar:has-text("INST HS")'), ).not.toBeVisible() await expect( - page.locator('.v-system-bar:has-text("INST COMMANDING")') + page.locator('.v-system-bar:has-text("INST COMMANDING")'), ).not.toBeVisible() await expect( - page.locator('.v-system-bar:has-text("INST GROUND")') + page.locator('.v-system-bar:has-text("INST GROUND")'), ).toBeVisible() await page.getByRole('button', { name: 'Close All' }).click() await expect( - page.locator('.v-system-bar:has-text("INST GROUND")') + page.locator('.v-system-bar:has-text("INST GROUND")'), ).not.toBeVisible() await expect( - page.locator('.v-system-bar:has-text("INST LAUNCHER")') + page.locator('.v-system-bar:has-text("INST LAUNCHER")'), ).not.toBeVisible() }) @@ -191,7 +191,7 @@ test('displays INST SIMPLE', async ({ page, utils }) => { await expect(page.locator(`text=${text}`)).toBeVisible() await page.locator('[data-test=edit-screen-icon]').click() await expect( - page.locator(`.v-system-bar:has-text("Edit Screen")`) + page.locator(`.v-system-bar:has-text("Edit Screen")`), ).toBeVisible() await utils.download( page, @@ -199,11 +199,11 @@ test('displays INST SIMPLE', async ({ page, utils }) => { function (contents) { expect(contents).toContain(`LABEL ${text}`) expect(contents).toContain('BIG INST HEALTH_STATUS TEMP2') - } + }, ) await page.locator('button:has-text("Cancel")').click() await expect( - page.locator(`.v-system-bar:has-text("Edit Screen")`) + page.locator(`.v-system-bar:has-text("Edit Screen")`), ).not.toBeVisible() }) }) @@ -220,7 +220,7 @@ test('creates new blank screen', async ({ page, utils }) => { await utils.sleep(500) await page.locator('[data-test=new-screen]').click() await expect( - page.locator(`.v-system-bar:has-text("New Screen")`) + page.locator(`.v-system-bar:has-text("New Screen")`), ).toBeVisible() // Spot check the list of existing screens await expect(page.locator(`.v-dialog:has-text("ADCS")`)).toBeVisible() @@ -230,12 +230,12 @@ test('creates new blank screen', async ({ page, utils }) => { // Check trying to create an existing screen await page.locator('[data-test=new-screen-name]').type('ADCS') await expect(page.locator('.v-dialog')).toContainText( - 'Screen ADCS already exists!' + 'Screen ADCS already exists!', ) await page.locator('[data-test=new-screen-name]').fill(screen) await page.locator('button:has-text("Ok")').click() await expect( - page.locator(`.v-system-bar:has-text("INST ${screen}")`) + page.locator(`.v-system-bar:has-text("INST ${screen}")`), ).toBeVisible() }) @@ -245,18 +245,18 @@ test('creates new screen based on packet', async ({ page, utils }) => { await utils.sleep(500) await page.locator('[data-test=new-screen]').click() await expect( - page.locator(`.v-system-bar:has-text("New Screen")`) + page.locator(`.v-system-bar:has-text("New Screen")`), ).toBeVisible() await page.locator('.v-dialog [data-test=new-screen-packet]').click() await page .locator(`div[role="option"] div:text-matches("HEALTH_STATUS")`) .click() expect(await page.inputValue('[data-test=new-screen-name]')).toMatch( - 'health_status' + 'health_status', ) await page.locator('button:has-text("Ok")').click() await expect( - page.locator(`.v-system-bar:has-text("INST HEALTH_STATUS")`) + page.locator(`.v-system-bar:has-text("INST HEALTH_STATUS")`), ).toBeVisible() }) @@ -271,13 +271,13 @@ async function deleteScreen(page, screen) { await page.locator('div[role="button"]:has-text("Select Screen")').click() await page.locator(`.v-list-item__title:text-is("${screen}")`).click() await expect( - page.locator(`.v-system-bar:has-text("INST ${screen}")`) + page.locator(`.v-system-bar:has-text("INST ${screen}")`), ).toBeVisible() await page.locator('[data-test=edit-screen-icon]').click() await page.locator('[data-test=delete-screen-icon]').click() await page.locator('button:has-text("Delete")').click() await page.locator('div[role="button"]:has-text("Select Screen")').click() await expect( - page.locator(`.v-list-item__title:text-is("${screen}")`) + page.locator(`.v-list-item__title:text-is("${screen}")`), ).not.toBeVisible() } diff --git a/playwright/tests/wait-for-build.spec.ts b/playwright/tests/wait-for-build.spec.ts index 6b291dc9ce..01051bfd4c 100644 --- a/playwright/tests/wait-for-build.spec.ts +++ b/playwright/tests/wait-for-build.spec.ts @@ -13,23 +13,26 @@ # GNU Affero General Public License for more details. # # Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. +# All changes Copyright 2023, OpenC3, Inc. # All Rights Reserved */ // @ts-check import { test, expect } from './fixture' -test('waits for the services to deploy and connect', async ({ page, utils }) => { +test('waits for the services to deploy and connect', async ({ + page, + utils, +}) => { await page.goto('/tools/cmdtlmserver') // Check the 3rd column (nth starts at 0) on the row containing INST_INT says CONNECTED await expect( - page.locator('tr:has-text("INST_INT") td >> nth=2') + page.locator('tr:has-text("INST_INT") td >> nth=2'), ).toContainText('CONNECTED', { timeout: 120000, }) await expect( - page.locator('tr:has-text("INST2_INT") td >> nth=2') + page.locator('tr:has-text("INST2_INT") td >> nth=2'), ).toContainText('CONNECTED', { timeout: 60000, })