Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dockerized action runners and script updates #3027

Draft
wants to merge 26 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f22f1b8
moved flipper search algorithm and units to testops.py
Aug 31, 2023
ba6ec39
typo in variable and decode for byte stream in unit parser
Aug 31, 2023
5b56269
deleted old scripts
Aug 31, 2023
0e1a99c
added notification badges and flash under reset for updater
Aug 31, 2023
7f3bc12
failure() checks for workflows
Aug 31, 2023
1ae55cf
updated workflows according to new device management scheme
Sep 5, 2023
4734d99
Merge branch 'dev' into doom/dockerized_action_runners
Sep 11, 2023
2323f5e
removed wrong status checks
Sep 11, 2023
916edf9
removed unnecessary reboot
Sep 11, 2023
eac1c57
debug output for port list
Sep 11, 2023
590efd5
enabled debug for workflows, expanded debug com port listing, enabled…
Sep 11, 2023
8049e2c
changed debug level
Sep 11, 2023
f517b77
fixed port list with symlink 0 and 1
Sep 11, 2023
ff4a1aa
fixed port list with symlink 0 and 1
Sep 11, 2023
9c53f50
Merge branch 'dev' into doom/dockerized_action_runners
Sep 11, 2023
2fda912
Merge remote-tracking branch 'origin/dev' into doom/dockerized_action…
Sep 18, 2023
8c374d5
Merge branch 'dev' into doom/dockerized_action_runners
Sep 22, 2023
cd11240
reverted unit and updater workflow, reversed scripts/testing files, m…
Oct 26, 2023
06de003
Merge branch 'dev' into doom/dockerized_action_runners
doomwastaken Oct 26, 2023
bb2e78a
code formatting for python
Oct 26, 2023
b7b448a
removed debug outputs
Oct 26, 2023
d659e8a
Merge branch 'dev' into doom/dockerized_action_runners
Nov 1, 2023
708cd03
updated resourse management, modified new build changes, merged dev
Nov 1, 2023
531b5c9
added proper trace for alternative workflow
Nov 1, 2023
2d4298d
Merge branch 'dev' into doom/dockerized_action_runners
doomwastaken Nov 13, 2023
94642bd
removed redundant port selection in test workflow
Nov 13, 2023
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
7 changes: 0 additions & 7 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: 'Unit tests'

on:
pull_request:

env:
TARGETS: f7
DEFAULT_TARGET: f7
Expand All @@ -15,7 +13,6 @@ jobs:
steps:
- name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;

- name: Checkout code
uses: actions/checkout@v4
with:
Expand All @@ -26,7 +23,6 @@ jobs:
id: device
run: |
echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT

- name: 'Flash unit tests firmware'
id: flashing
if: success()
Expand All @@ -42,7 +38,6 @@ jobs:
source scripts/toolchain/fbtenv.sh
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext

- name: 'Copy assets and unit data, reboot and wait for flipper'
id: copy
if: steps.format_ext.outcome == 'success'
Expand All @@ -54,15 +49,13 @@ jobs:
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send build/latest/resources /ext
python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}

- name: 'Run units and validate results'
id: run_units
if: steps.copy.outcome == 'success'
timeout-minutes: 7
run: |
source scripts/toolchain/fbtenv.sh
python3 scripts/testing/units.py ${{steps.device.outputs.flipper}}

- name: 'Check GDB output'
if: failure()
run: |
Expand Down
8 changes: 0 additions & 8 deletions .github/workflows/updater_test.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: 'Updater test'

on:
pull_request:

env:
TARGETS: f7
DEFAULT_TARGET: f7
Expand All @@ -15,7 +13,6 @@ jobs:
steps:
- name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;

- name: Checkout code
uses: actions/checkout@v4
with:
Expand All @@ -28,15 +25,13 @@ jobs:
run: |
echo "flipper=Rekigyn" >> $GITHUB_OUTPUT
echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT

- name: 'Flashing target firmware'
id: first_full_flash
timeout-minutes: 10
run: |
source scripts/toolchain/fbtenv.sh
./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}

- name: 'Validating updater'
id: second_full_flash
timeout-minutes: 10
Expand All @@ -45,13 +40,11 @@ jobs:
source scripts/toolchain/fbtenv.sh
./fbt flash_usb PORT=${{steps.device.outputs.flipper}} FORCE=1
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}

- name: 'Get last release tag'
id: release_tag
if: failure()
run: |
echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT

- name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;

Expand All @@ -66,7 +59,6 @@ jobs:
if: failure()
run: |
./fbt flash SWD_TRANSPORT_SERIAL=${{steps.device.outputs.stlink}} FORCE=1

- name: 'Wait for flipper and format ext'
if: failure()
run: |
Expand Down
93 changes: 93 additions & 0 deletions .github/workflows/workflow_testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: 'Testing workflows'

on:
pull_request:

env:
TARGETS: f7
DEFAULT_TARGET: f7
FBT_TOOLCHAIN_PATH: /opt
ACTIONS_RUNNER_DEBUG: true

jobs:
unit_tests_dockerized:
runs-on: [self-hosted, FlipperZeroIntegrationTest]
steps:
- name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;

- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1
ref: ${{ github.event.pull_request.head.sha }}

- name: 'Flash unit tests firmware'
id: flashing
if: success()
timeout-minutes: 10
run: |
source scripts/toolchain/fbtenv.sh
./fbt resources firmware_latest flash LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1
python3 scripts/testops.py await_flipper

- name: 'Copy assets and unit data, reboot and wait for flipper'
id: copy
timeout-minutes: 7
run: |
source scripts/toolchain/fbtenv.sh
rm -rf build/latest/resources/dolphin
python3 scripts/storage.py -f send build/latest/resources /ext

- name: 'Run units and validate results'
id: run_units
timeout-minutes: 7
run: |
source scripts/toolchain/fbtenv.sh
python3 scripts/testops.py run_units

- name: 'Check GDB output'
if: failure()
run: |
./fbt gdb_trace_all FIRMWARE_APP_SET=unit_tests LIB_DEBUG=1 FORCE=1

- name: 'Create notification'
if: failure()
run: |
echo "::error ::Unit tests failed, check that step for details"


updater_tests_dockerized:
runs-on: [self-hosted, FlipperZeroIntegrationTest]
steps:
- name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;

- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 1
submodules: false
ref: ${{ github.event.pull_request.head.sha }}

- name: 'Flashing target firmware'
id: first_full_flash
timeout-minutes: 10
run: |
source scripts/toolchain/fbtenv.sh
./fbt flash_usb_full FORCE=1
python3 scripts/testops.py await_flipper

- name: 'Validating updater'
id: second_full_flash
timeout-minutes: 5
run: |
source scripts/toolchain/fbtenv.sh
./fbt flash_usb_full FORCE=1
python3 scripts/testops.py await_flipper

- name: 'Create notification'
if: failure()
run: |
echo "::error ::Flipper updater failure"

Empty file modified scripts/testing/await_flipper.py
100755 → 100644
Empty file.
Empty file modified scripts/testing/units.py
100755 → 100644
Empty file.
126 changes: 126 additions & 0 deletions scripts/testops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env python3

import re
import sys
import time
from typing import Optional

from flipper.app import App
from flipper.storage import FlipperStorage
from flipper.utils.cdc import resolve_port


class Main(App):
# this is basic use without sub-commands, simply to reboot flipper / power it off, not meant as a full CLI wrapper
def init(self):
self.parser.add_argument("-p", "--port", help="CDC Port", default="auto")

self.subparsers = self.parser.add_subparsers(help="sub-command help")

self.parser_await_flipper = self.subparsers.add_parser(
"await_flipper", help="Wait for Flipper to connect or reconnect"
)
self.parser_await_flipper.set_defaults(func=self.await_flipper)

self.parser_run_units = self.subparsers.add_parser(
"run_units", help="Run unit tests and post result"
)
self.parser_run_units.set_defaults(func=self.run_units)

def _get_flipper(self, retry_count: Optional[int] = 1):
port = None
self.logger.info(f"Attempting to find flipper with {retry_count} attempts.")

for i in range(retry_count):
self.logger.info(f"Attempting to find flipper #{i}.")

if port := resolve_port(self.logger, self.args.port):
self.logger.info(f"Found flipper at {port}")
break
time.sleep(1)

if not port:
self.logger.info(f"Failed to find flipper")
return None

flipper = FlipperStorage(port)
flipper.start()
return flipper

def await_flipper(self):
if not (flipper := self._get_flipper(retry_count=90)):
return 1

self.logger.info("Flipper started")
flipper.stop()
return 0

def run_units(self):
if not (flipper := self._get_flipper(retry_count=10)):
return 1

self.logger.info("Running unit tests")
flipper.send("unit_tests" + "\r")
self.logger.info("Waiting for unit tests to complete")
data = flipper.read.until(">: ")
self.logger.info("Parsing result")

lines = data.decode().split("\r\n")

tests_re = r"Failed tests: \d{0,}"
time_re = r"Consumed: \d{0,}"
leak_re = r"Leaked: \d{0,}"
status_re = r"Status: \w{3,}"

tests_pattern = re.compile(tests_re)
time_pattern = re.compile(time_re)
leak_pattern = re.compile(leak_re)
status_pattern = re.compile(status_re)

tests, elapsed_time, leak, status = None, None, None, None
total = 0

for line in lines:
self.logger.info(line)
if "()" in line:
total += 1

if not tests:
tests = re.match(tests_pattern, line)
if not elapsed_time:
elapsed_time = re.match(time_pattern, line)
if not leak:
leak = re.match(leak_pattern, line)
if not status:
status = re.match(status_pattern, line)

if None in (tests, elapsed_time, leak, status):
self.logger.error(
f"Failed to parse output: {tests} {elapsed_time} {leak} {status}"
)
sys.exit(1)

leak = int(re.findall(r"[- ]\d+", leak.group(0))[0])
status = re.findall(r"\w+", status.group(0))[1]
tests = int(re.findall(r"\d+", tests.group(0))[0])
elapsed_time = int(re.findall(r"\d+", elapsed_time.group(0))[0])

if tests > 0 or status != "PASSED":
self.logger.error(f"Got {tests} failed tests.")
self.logger.error(f"Leaked (not failing on this stat): {leak}")
self.logger.error(f"Status: {status}")
self.logger.error(f"Time: {elapsed_time/1000} seconds")
flipper.stop()
return 1

self.logger.info(f"Leaked (not failing on this stat): {leak}")
self.logger.info(
f"Tests ran successfully! Time elapsed {elapsed_time/1000} seconds. Passed {total} tests."
)

flipper.stop()
return 0


if __name__ == "__main__":
Main()()
Loading