Skip to content

Commit

Permalink
Fix python unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jmthomas committed Feb 8, 2025
1 parent b31567f commit 13d3cd2
Show file tree
Hide file tree
Showing 10 changed files with 369 additions and 216 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/api_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ jobs:
working-directory: openc3/python
- name: Lint with ruff
run: |
poetry run ruff --config=../openc3/python/pyproject.toml --output-format=github scripts/*.py
working-directory: openc3-cosmos-script-runner-api
poetry run ruff check --output-format=github ../../openc3-cosmos-script-runner-api/scripts
working-directory: openc3/python
- name: Run unit tests
run: |
poetry run coverage run -m pytest ./test/
poetry run coverage run -m pytest ../../openc3-cosmos-script-runner-api/test/
poetry run coverage xml -i
working-directory: openc3-cosmos-script-runner-api
working-directory: openc3/python
- uses: codecov/codecov-action@v5
with:
working-directory: openc3/python
Expand Down
11 changes: 5 additions & 6 deletions openc3-cosmos-script-runner-api/scripts/run_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,27 @@
import time
import json
import sys
import traceback
from datetime import datetime, timezone
from openc3.script import get_overrides
from openc3.utilities.bucket import Bucket
from openc3.utilities.store import Store, EphemeralStore
from openc3.utilities.extract import convert_to_value
from openc3.utilities.logger import Logger
from openc3.environment import *
import traceback
from openc3.environment import OPENC3_CONFIG_BUCKET
from running_script import RunningScript, running_script_anycable_publish

start_time = time.time()

from running_script import RunningScript, running_script_anycable_publish

# # Load the bucket client code to ensure we authenticate outside ENV vars
# Load the bucket client code to ensure we authenticate outside ENV vars
Bucket.getClient()

del os.environ["OPENC3_BUCKET_USERNAME"]
del os.environ["OPENC3_BUCKET_PASSWORD"]
os.unsetenv("OPENC3_BUCKET_USERNAME")
os.unsetenv("OPENC3_BUCKET_PASSWORD")

# # Preload Store and remove Redis secrets from ENV
# Preload Store and remove Redis secrets from ENV
Store.instance()
EphemeralStore.instance()

Expand Down
25 changes: 13 additions & 12 deletions openc3-cosmos-script-runner-api/scripts/running_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -1214,18 +1214,19 @@ def redirect_io(self):
Logger.stdout = True
Logger.level = Logger.INFO

def output_thread(self):
RunningScript.cancel_output = False
RunningScript.output_sleeper = Sleeper()
while True:
if RunningScript.cancel_output:
break
if (time.time() - self.output_time) > 5.0:
self.handle_output_io()
if RunningScript.cancel_output:
break
if RunningScript.output_sleeper.sleep(1.0):
break
# TODO: This is defined on 206 ... so this is not called
# def output_thread(self):
# RunningScript.cancel_output = False
# RunningScript.output_sleeper = Sleeper()
# while True:
# if RunningScript.cancel_output:
# break
# if (time.time() - self.output_time) > 5.0:
# self.handle_output_io()
# if RunningScript.cancel_output:
# break
# if RunningScript.output_sleeper.sleep(1.0):
# break


openc3.script.RUNNING_SCRIPT = RunningScript
Expand Down
Empty file.
5 changes: 4 additions & 1 deletion openc3-ruby/Dockerfile-ubi
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ RUN cd / \
&& python3 -m venv /openc3/venv \
&& source /openc3/venv/bin/activate \
&& pip3 config --global set global.index $PYPI_URL/pypi \
&& pip3 config --global set global.index-url $PYPI_URL/simple
&& pip3 config --global set global.index-url $PYPI_URL/simple \
&& pip3 install --upgrade pip setuptools \
&& pip3 install poetry \
&& pip3 install poetry-plugin-export

# Begin CVE fix CVE-2023-36617 (update uri 0.12.1 to version 0.12.2 or greater)

Expand Down
5 changes: 4 additions & 1 deletion openc3/python/openc3/packets/packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,10 @@ def buffer(self, buffer):
with self.synchronize():
try:
self.internal_buffer_equals(buffer)
except Exception:
# Catch and re-raise the TypeError thrown by internal_buffer_equals
except TypeError as error:
raise error
except ValueError:
Logger.error(
f"{self.target_name} {self.packet_name} buffer ({type(buffer)}) received with actual packet length of {len(buffer)} but defined length of {self.defined_length}"
)
Expand Down
190 changes: 4 additions & 186 deletions openc3/python/openc3/packets/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def warnings(self):

# @return [Array<String>] The command target names (excluding UNKNOWN)
def target_names(self):
result = self.config.telemetry.keys()
result.delete("UNKNOWN")
result = list(self.config.telemetry.keys())
result.remove("UNKNOWN")
result.sort()
return result

Expand Down Expand Up @@ -70,158 +70,6 @@ def packet(self, target_name, packet_name):
raise RuntimeError(f"Telemetry packet '{upcase_target_name} {upcase_packet_name}' does not exist")
return packet

# # @param target_name (see #packet)
# # @param packet_name [String] The packet name. 'LATEST' can also be given
# # to specify the last received (or defined if no packets have been
# # received) packet within the given target that contains the
# # item_name.
# # @param item_name [String] The item name
# # @return [Packet, PacketItem] The packet and the packet item
# def packet_and_item(target_name, packet_name, item_name):
# upcase_packet_name = str(packet_name).upper()
# if upcase_packet_name == "LATEST":
# return_packet = newest_packet(target_name, item_name)
# else:
# return_packet = packet(target_name, packet_name)
# item = return_packet.get_item(item_name)
# return [return_packet, item]

# # Return a telemetry value from a packet.
# #
# # @param target_name (see #packet_and_item)
# # @param packet_name (see #packet_and_item)
# # @param item_name (see #packet_and_item)
# # @param value_type [Symbol] How to convert the item before returning.
# # Must be one of {Packet::VALUE_TYPES}
# # @return The value. :FORMATTED and :WITH_UNITS values are always returned
# # as Strings. :RAW values will match their data_type. :CONVERTED values
# # can be any type.
# def value(target_name, packet_name, item_name, value_type = 'CONVERTED'):
# packet, _ = packet_and_item(target_name, packet_name, item_name) # Handles LATEST
# return packet.read(item_name, value_type)

# # Reads the specified list of items and returns their values and limits
# # state.
# #
# # @param item_array [Array<Array(String String String)>] An array
# # consisting of [target name, packet name, item name]
# # @param value_types [Symbol|Array<Symbol>] How to convert the items before
# # returning. A single symbol of {Packet::VALUE_TYPES}
# # can be passed which will convert all items the same way. Or
# # an array of symbols can be passed to control how each item is
# # converted.
# # @return [Array, Array, Array] The first array contains the item values and the
# # second their limits state, and the third their limits settings which includes
# # the red, yellow, and green (if given) limits values.
# def values_and_limits_states(item_array, value_types = 'CONVERTED'):
# items = []

# # Verify item_array is a nested array
# raise ValueError(f"item_array must be a nested array consisting of [[tgt,pkt,item],[tgt,pkt,item],...]") if not Array === item_array[0]

# states = []
# settings = []
# limits_set = System.limits_set()

# if (Array === value_types) and len(item_array) != len(value_types):
# raise ValueError(f"Passed {len(item_array)} items but only {len(value_types)} value types")

# value_type = value_types if not Array === value_types
# len(item_array).times do |index|
# entry = item_array[index]
# target_name = entry[0]
# packet_name = entry[1]
# item_name = entry[2]
# if Array === value_types:
# value_type = value_types[index]

# packet, item = packet_and_item(target_name, packet_name, item_name) # Handles LATEST
# items.append(packet.read(item_name, value_type))
# limits = item.limits
# states.append(limits.state)
# limits_values = limits.values
# if limits_values:
# limits_settings = limits_values[limits_set]
# else:
# limits_settings = None
# settings.append(limits_settings)

# return [items, states, settings]

# # @param target_name (see #packet)
# # @param packet_name (see #packet)
# # @return [Array<PacketItem>] The telemetry items for the given target and packet name
# def items(target_name, packet_name):
# return packet(target_name, packet_name).sorted_items

# # @param target_name (see #packet)
# # @param packet_name (see #packet) The packet name. LATEST is supported.
# # @return [Array<PacketItem>] The telemetry item names for the given target and packet name
# def item_names(target_name, packet_name):
# if LATEST_PACKET_NAME.casecmp(packet_name).zero?:
# target_upmatch = str(target_name).upper():
# target_latest_data = self.config.latest_data[target_upcase]
# raise "Telemetry Target '{target_upcase}' does not exist" if not target_latest_data

# item_names = target_latest_data.keys
# else:
# tlm_packet = packet(target_name, packet_name)
# item_names = []
# tlm_packet.sorted_items.each { |item| item_names.append(item.name })
# item_names

# # Set a telemetry value in a packet.
# #
# # @param target_name (see #packet_and_item)
# # @param packet_name (see #packet_and_item)
# # @param item_name (see #packet_and_item)
# # @param value The value to set in the packet
# # @param value_type (see #tlm)
# def set_value(target_name, packet_name, item_name, value, value_type = 'CONVERTED'):
# packet, _ = packet_and_item(target_name, packet_name, item_name)
# packet.write(item_name, value, value_type)

# # @param target_name (see #packet_and_item)
# # @param item_name (see #packet_and_item)
# # @return [Array<Packet>] The latest (most recently arrived) packets with
# # the specified target and item.
# def latest_packets(target_name, item_name):
# target_upmatch = str(target_name).upper():
# item_upmatch = str(item_name).upper():
# target_latest_data = self.config.latest_data[target_upcase]
# raise "Telemetry target '{target_upcase}' does not exist" if not target_latest_data

# packets = self.config.latest_data[target_upcase][item_upcase]
# raise "Telemetry item '{target_upcase} {LATEST_PACKET_NAME} {item_upcase}' does not exist" if not packets

# return packets

# # @param target_name (see #packet_and_item)
# # @param item_name (see #packet_and_item)
# # @return [Packet] The packet with the most recent timestamp that contains
# # the specified target and item.
# def newest_packet(target_name, item_name):
# # Handle LATEST_PACKET_NAME - Lookup packets for this target/item
# packets = latest_packets(target_name, item_name)

# # Find packet with newest timestamp
# newest_packet = None
# newest_received_time = None
# for packet in packets:
# received_time = packet.received_time
# if newest_received_time:
# # See if the received time from this packet is newer.
# # Having the >= makes this method return the last defined packet
# # whether the timestamps are both nil or both equal.
# if received_time and received_time >= newest_received_time:
# newest_packet = packet
# newest_received_time = newest_packet.received_time
# else:
# # No received time yet so take this packet
# newest_packet = packet
# newest_received_time = newest_packet.received_time
# return newest_packet

# Identifies an unknown buffer of data as a defined packet and sets the
# packet's data to the given buffer. Identifying a packet uses the fields
# marked as ID_ITEM to identify if the buffer passed represents the
Expand Down Expand Up @@ -263,10 +111,10 @@ def identify(self, packet_data, target_names=None):
# No telemetry for this target
continue

target = self.system.targets[target_name]
target = self.system.targets.get(target_name)
if target and target.tlm_unique_id_mode:
# Iterate through the packets and see if any represent the buffer
for _, packet in target_packets:
for _, packet in target_packets.items():
if packet.identify(packet_data):
return packet
else:
Expand Down Expand Up @@ -339,36 +187,6 @@ def reset(self):
for _, packet in packets.items():
packet.reset()

# # Returns an array with a "TARGET_NAME PACKET_NAME ITEM_NAME" string for every item in the system
# def all_item_strings(include_hidden = False, splash = None):
# strings = []
# tnames = target_names()
# total = len(tnames) float()
# tnames.each_with_index do |target_name, index|
# if splash:
# splash.message = "Processing {target_name} telemetry"
# splash.progress = index / total

# # Note: System only has declared target structures but telemetry may have more
# system_target = System.targets[target_name]
# if system_target:
# ignored_items = system_target.ignored_items
# else:
# ignored_items = []

# for packet_name, packet in packets(target_name):
# # We don't audit against hidden or disabled packets
# if !include_hidden and (packet.hidden or packet.disabled):
# next

# packet.items.each_key do |item_name|
# # Skip ignored items
# if !include_hidden and ignored_items.include? item_name:
# next

# strings.append("{target_name} {packet_name} {item_name}")
# return strings

# @return [Hash{String=>Hash{String=>Packet}}] Hash of all the telemetry
# packets keyed by the target name. The value is another hash keyed by the
# packet name returning the packet.
Expand Down
10 changes: 4 additions & 6 deletions openc3/python/test/api/test_interface_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def disconnect(self):
pass

def read_interface(self):
time.sleep(0.05)
time.sleep(0.01)
return (b"\x01\x02\x03\x04", None)

def interface_cmd(self, cmd_name, *cmd_params):
Expand Down Expand Up @@ -77,7 +77,7 @@ def build():
self.im = InterfaceMicroservice("DEFAULT__INTERFACE__INST_INT")
self.im_thread = threading.Thread(target=self.im.run)
self.im_thread.start()
time.sleep(0.01) # Allow the thread to run
time.sleep(0.02) # Allow the thread to run

def tearDown(self):
self.im.shutdown()
Expand All @@ -88,7 +88,7 @@ def test_returns_interface_hash(self):
self.assertEqual(type(interface), dict)
self.assertEqual(interface["name"], "INST_INT")
# Verify it also includes the status
self.assertEqual(interface["state"], "ATTEMPTING")
self.assertEqual(interface["state"], "CONNECTED")
self.assertEqual(interface["clients"], 0)

def test_returns_all_interface_names(self):
Expand All @@ -99,8 +99,6 @@ def test_returns_all_interface_names(self):
self.assertEqual(get_interface_names(), ["INST_INT", "INT1", "INT2"])

def test_connects_the_interface(self):
self.assertEqual(get_interface("INST_INT")["state"], "ATTEMPTING")
time.sleep(0.1)
self.assertEqual(get_interface("INST_INT")["state"], "CONNECTED")
disconnect_interface("INST_INT")
time.sleep(0.1)
Expand Down Expand Up @@ -134,7 +132,7 @@ def test_should_start_and_stop_raw_logging_on_the_interface(self):
def test_gets_interface_name_and_all_info(self):
info = get_all_interface_info()
self.assertEqual(info[0][0], "INST_INT")
self.assertEqual(info[0][1], "ATTEMPTING")
self.assertEqual(info[0][1], "CONNECTED")

def test_successfully_maps_a_target_to_an_interface(self):
TargetModel(name="INST", scope="DEFAULT").create()
Expand Down
Loading

0 comments on commit 13d3cd2

Please sign in to comment.