Skip to content

Commit

Permalink
Add example code of a blinking led
Browse files Browse the repository at this point in the history
  • Loading branch information
jmaralo committed Jan 19, 2025
1 parent ad486d5 commit 1e1dfd3
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
url = https://github.com/HyperloopUPV-H8/ST-LIB
[submodule "Core/Inc/Communications/JSON_ADE"]
path= Core/Inc/Communications/JSON_ADE
url = https://github.com/HyperloopUPV-H8/JSON_ADE
url = https://github.com/HyperloopUPV-H8/JSON_ADE
[submodule "Tests/VirtualMCU"]
path = Tests/VirtualMCU
url = https://github.com/HyperloopUPV-H8/VirtualMCU
7 changes: 7 additions & 0 deletions Core/Src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@
#include "ST-LIB.hpp"

int main(void) {
#ifdef SIM_ON
SharedMemory::start();
#endif

DigitalOutput led_on(PA1);
STLIB::start();

Time::register_low_precision_alarm(100, [&]() { led_on.toggle(); });

while (1) {
STLIB::update();
}
Expand Down
1 change: 1 addition & 0 deletions Tests/VirtualMCU
Submodule VirtualMCU added at fac66f
138 changes: 138 additions & 0 deletions Tests/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import subprocess
from argparse import ArgumentParser
import time


class DuplicatedTestError(Exception):
def __init__(self, name):
self._name = name

def __str__(self):
return f"Test {self._name} is duplicated"


class UnitUnderTest:
def __init__(self, executable):
self._executable = executable

def __enter__(self):
self._process = subprocess.Popen(
self._executable,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)

def __exit__(self, *args):
try:
out, err = self._process.communicate(timeout=1)
except Exception:
self._process.kill()
try:
out, err = self._process.communicate(timeout=1)
except Exception:
out = "Error recovering stdout"
err = "Error recovering stderr"

if out:
print(f" * UUT stdout:\n{out}")
if err:
print(f" * UUT stderr:\n{err}")


class Test:
def __init__(self, func):
self._func = func
self._prepare = None
self._cleanup = None

def __call__(self, *args, **kwargs):
return self._func(*args, **kwargs)

def prepare(self):
def decorator(prepare_func):
nonlocal self
self._prepare = prepare_func
return prepare_func

return decorator

def cleanup(self):
def decorator(cleanup_func):
nonlocal self
self._cleanup = cleanup_func
return cleanup_func

return decorator

def run_prepare(self):
if self._prepare is not None:
self._prepare()

def run_cleanup(self):
if self._cleanup is not None:
self._cleanup()

class TestRunner:

def __init__(self, uut_executable):
self._prepare = {}
self._tests = {}
self._cleanup = {}
self._uut = UnitUnderTest(uut_executable)

# Registers a new test in the runner, name is infered from the function name
def test(self):
def decorator(test_func):
nonlocal self

if test_func.__name__ in self._tests:
raise DuplicatedTestError(test_func.__name__)

self._tests[test_func.__name__] = Test(test_func)

return self._tests[test_func.__name__]

return decorator


# Runs all the registered tests, cleaning up after each test
def run(self):
for name, test in self._tests.items():
try:
test.run_prepare()

with self._uut:
time.sleep(0.1)
try:
print(f"[{name}] Running...")
result = test()
print(f"[{name}] Succesfull!")
if result is not None:
print(f" * Result: {result}")
except Exception as reason:
print(f"[{name}] Failed!")
print(f" * Reason: {reason}")

test.run_cleanup()
except KeyboardInterrupt:
print(f"[{name}] Keyboard Interrupt. Aborted.")



parser = ArgumentParser(
prog="test",
description="run multiple simulator tests on a target executable"
)

parser.add_argument(
"-uut",
"--executable",
required=True,
help="full path to the target executable"
)

args = parser.parse_args()

runner = TestRunner(args.executable)
50 changes: 50 additions & 0 deletions Tests/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), "VirtualMCU", "src"))

from runner import runner
from vmcu.shared_memory import SharedMemory
from vmcu.pin import Pinout, DigitalOut
from vmcu.services.digital_out import DigitalOutService
from vmcu.assertions import *


@runner.test()
def led_toggle():
TOGGLE_PERIOD = milliseconds(100 * 2)
ALLOWED_SLACK = milliseconds(5)

shm = SharedMemory("gpio__blinking_led", "state_machine__blinking_led")
led = DigitalOutService(shm, Pinout.PA1)

def led_turns_on():
nonlocal led
return led.get_pin_state() is DigitalOut.State.High

def led_turns_off():
nonlocal led
return led.get_pin_state() is DigitalOut.State.Low

#sync with board
completes(
wait_until_true(led_turns_on),
before=(TOGGLE_PERIOD / 2) + ALLOWED_SLACK,
msg="Sync fails"
)

for i in range(150):
completes(
wait_until_true(led_turns_off),
before=(TOGGLE_PERIOD / 2) + ALLOWED_SLACK,
after=(TOGGLE_PERIOD / 2) - ALLOWED_SLACK,
msg="turns off"
)
completes(
wait_until_true(led_turns_on),
before=(TOGGLE_PERIOD / 2) + ALLOWED_SLACK,
after=(TOGGLE_PERIOD / 2) - ALLOWED_SLACK,
msg="turns on"
)
print("toggle", i)


runner.run() # Runs the tests, do not delete!

0 comments on commit 1e1dfd3

Please sign in to comment.