Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ jobs:
testrun_tests:
name: Tests
runs-on: ubuntu-20.04
needs: testrun_baseline
timeout-minutes: 40
steps:
- name: Checkout source
uses: actions/checkout@v2.3.4
- name: Run tests
shell: bash {0}
run: testing/tests/test_tests

pylint:
name: Pylint
runs-on: ubuntu-22.04
Expand Down
24 changes: 17 additions & 7 deletions framework/python/src/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self, test_run):
self._router.add_api_route("/system/stop", self.stop_test_run,
methods=["POST"])
self._router.add_api_route("/system/status", self.get_status)

self._router.add_api_route("/history", self.get_history)
self._router.add_api_route("/devices", self.get_devices)
self._router.add_api_route("/device", self.save_device, methods=["POST"])

Expand Down Expand Up @@ -126,7 +126,6 @@ async def start_test_run(self, request: Request, response: Response):
return self._generate_msg(False, "Invalid request received")

device = self._session.get_device(body_json["device"]["mac_addr"])
device.firmware = body_json["device"]["firmware"]

# Check Test Run is not already running
if self._test_run.get_session().get_status() != "Idle":
Expand All @@ -140,11 +139,14 @@ async def start_test_run(self, request: Request, response: Response):
return self._generate_msg(False,
"A device with that MAC address could not be found")

device.firmware = body_json["device"]["firmware"]

# Check Test Run is able to start
if self._test_run.get_net_orc().check_config() is False:
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
return self._generate_msg(False,"Configured interfaces are not ready for use. Ensure required interfaces are connected.")

self._test_run.get_session().reset()
self._test_run.get_session().set_target_device(device)
LOGGER.info(f"Starting Test Run with device target {device.manufacturer} {device.model} with MAC address {device.mac_addr}")

Expand All @@ -171,7 +173,8 @@ async def get_status(self):
return self._test_run.get_session().to_json()

async def get_history(self):
LOGGER.info("Returning previous Test Runs to UI")
LOGGER.debug("Received history list request")
return self._session.get_all_reports()

async def save_device(self, request: Request, response: Response):
LOGGER.debug("Received device post request")
Expand All @@ -185,18 +188,25 @@ async def save_device(self, request: Request, response: Response):
return self._generate_msg(False, "Invalid request received")

device = self._session.get_device(device_json.get(DEVICE_MAC_ADDR_KEY))

if device is None:

# Create new device
device = Device()
device.mac_addr = device_json.get(DEVICE_MAC_ADDR_KEY)
device.manufacturer = device_json.get(DEVICE_MANUFACTURER_KEY)
device.model = device_json.get(DEVICE_MODEL_KEY)
device.device_folder = device.manufacturer + " " + device.model

self._test_run.create_device(device)
response.status_code = status.HTTP_201_CREATED

device.manufacturer = device_json.get(DEVICE_MANUFACTURER_KEY)
device.model = device_json.get(DEVICE_MODEL_KEY)
else:

self._session.save_device(device)
self._test_run.save_device(device, device_json)
response.status_code = status.HTTP_200_OK

return device
return device.to_config_json()

# Catch JSON Decode error etc
except JSONDecodeError:
Expand Down
26 changes: 24 additions & 2 deletions framework/python/src/common/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

"""Track device object information."""

from dataclasses import dataclass
from typing import Dict
from dataclasses import dataclass, field

@dataclass
class Device():
Expand All @@ -24,17 +25,38 @@ class Device():
mac_addr: str = None
manufacturer: str = None
model: str = None
test_modules: str = None
test_modules: Dict = field(default_factory=dict)
ip_addr: str = None
firmware: str = None
device_folder: str = None
reports = []
max_device_reports: int = None

def add_report(self, report):
self.reports.append(report)

def get_reports(self):
return self.reports

# TODO: Add ability to remove reports once test reports have been cleaned up

def to_json(self):
"""Returns the device as a python dictionary. This is used for the
# system status API endpoint and in the report."""
Comment on lines 43 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these references to json be changed to dict since these aren't really returning json?

device_json = {}
device_json['mac_addr'] = self.mac_addr
device_json['manufacturer'] = self.manufacturer
device_json['model'] = self.model
if self.firmware is not None:
device_json['firmware'] = self.firmware
return device_json

def to_config_json(self):
"""Returns the device as a python dictionary. Fields relevant to the device
config json file are exported."""
device_json = {}
device_json['mac_addr'] = self.mac_addr
device_json['manufacturer'] = self.manufacturer
device_json['model'] = self.model
device_json['test_modules'] = self.test_modules
return device_json
59 changes: 46 additions & 13 deletions framework/python/src/common/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,14 @@ def __init__(self, config_file):
self._finished = None
self._results = []
self._runtime_params = []

self._device_repository = []
self._total_tests = 0
self._config_file = config_file

self._config = self._get_default_config()
self._load_config()

self._device_repository = []

def start(self):
self._status = 'Starting'
self._status = 'Waiting for device'
self._started = datetime.datetime.now()

def get_started(self):
Expand Down Expand Up @@ -136,7 +134,7 @@ def get_monitor_period(self):

def get_startup_timeout(self):
return self._config.get(STARTUP_TIMEOUT_KEY)

def get_api_port(self):
return self._config.get(API_PORT_KEY)

Expand All @@ -159,16 +157,15 @@ def get_device_repository(self):
def add_device(self, device):
self._device_repository.append(device)

def clear_device_repository(self):
self._device_repository = []

def get_device(self, mac_addr):
for device in self._device_repository:
if device.mac_addr == mac_addr:
return device
return None

def save_device(self, device):
# TODO: We need to save the folder path of the device config
return

def get_status(self):
return self._status

Expand All @@ -178,21 +175,57 @@ def set_status(self, status):
def get_test_results(self):
return self._results

def get_report_tests(self):
return {
'total': self.get_total_tests(),
'results': self.get_test_results()
}

def add_test_result(self, test_result):
self._results.append(test_result)

def get_all_reports(self):

reports = []

for device in self.get_device_repository():
device_reports = device.get_reports()
for device_report in device_reports:
reports.append(device_report.to_json())

return reports

def add_total_tests(self, no_tests):
self._total_tests += no_tests

def get_total_tests(self):
return self._total_tests

def reset(self):
self.set_status('Idle')
self.set_target_device(None)
self._results = []
self._tests = {
'total': 0,
'results': []
}
self._started = None
self._finished = None

def to_json(self):
return {

# TODO: Add report URL

results = {
'total': self.get_total_tests(),
'results': self.get_test_results()
}

session_json = {
'status': self.get_status(),
'device': self.get_target_device(),
'started': self.get_started(),
'finished': self.get_finished(),
'results': self.get_test_results()
'tests': results
}

return session_json
84 changes: 84 additions & 0 deletions framework/python/src/common/testreport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Store previous test run information."""

from datetime import datetime

DATE_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'

class TestReport():
"""Represents a previous Test Run report."""

def __init__(self,
status='Non-Compliant',
started=None,
finished=None,
total_tests=0
):
self._device = {}
self._status: str = status
self._started = started
self._finished = finished
self._total_tests = total_tests
self._results = []

def get_status(self):
return self._status

def get_started(self):
return self._started

def get_finished(self):
return self._finished

def get_duration_seconds(self):
diff = self._finished - self._started
return diff.total_seconds()

def get_duration(self):
return str(datetime.timedelta(seconds=self.get_duration_seconds()))

def add_test(self, test):
self._results.append(test)

def to_json(self):
report_json = {}
report_json['device'] = self._device
report_json['status'] = self._status
report_json['started'] = self._started.strftime(DATE_TIME_FORMAT)
report_json['finished'] = self._finished.strftime(DATE_TIME_FORMAT)
report_json['tests'] = {'total': self._total_tests,
'results': self._results}
return report_json

def from_json(self, json_file):

self._device['mac_addr'] = json_file['device']['mac_addr']
self._device['manufacturer'] = json_file['device']['manufacturer']
self._device['model'] = json_file['device']['model']

if 'firmware' in self._device:
self._device['firmware'] = json_file['device']['firmware']

self._status = json_file['status']
self._started = datetime.strptime(json_file['started'], DATE_TIME_FORMAT)
self._finished = datetime.strptime(json_file['finished'], DATE_TIME_FORMAT)
self._total_tests = json_file['tests']['total']

# Loop through test results
for test_result in json_file['tests']['results']:
self.add_test(test_result)

return self
Loading