Skip to content

Commit

Permalink
🧪 test(Python POC): Add autotest
Browse files Browse the repository at this point in the history
Add autotest for bpif3-armbian
  • Loading branch information
wychlw committed Sep 12, 2024
1 parent 25c5d4e commit c580abb
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 0 deletions.
31 changes: 31 additions & 0 deletions apps/boards/boards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
This module contains the abstract class for all boards.
"""

from abc import ABC, abstractmethod
from tester import PyTty


class Boards(ABC):
"""
Abstract class for all boards.
This is mainly an interface definition, instead of a base class.
"""
@abstractmethod
def flash(self, img: str, *args, **kwargs):
"""
Flash the board with given image.
"""

@abstractmethod
def power_cycle(self, *args, **kwargs):
"""
Power cycle the board.
Basically, trigger a reboot or reset to the board.
"""

@abstractmethod
def get_console(self, *args, **kwargs) -> PyTty:
"""
Get the console of the board.
"""
45 changes: 45 additions & 0 deletions apps/boards/bpif3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
For board banana pi f3
"""

from time import sleep
from tester import PyTty, PySerial, PySdWirec, PyExec


class BPiF3:
"""
Board class for Banana Pi F3.
"""

def __init__(self, sdwirec_port="id = 0\n", serial_port="/dev/ttyUSB0", baud=115200) -> None:
self.sdwirec = PySdWirec(sdwirec_port)
self.serial_port = serial_port
self.baud = baud

def flash(self, shell: PyExec, img: str, dsk="/dev/sda"):
"""
Flash the board with given image.
"""
self.sdwirec.to_ts()
sleep(0.5)
shell.assert_script_sudo(
f"dd if={img} of={dsk} bs=1M status=progress", 600)
sleep(0.5)
self.sdwirec.to_dut()
sleep(0.5)

def power_cycle(self):
"""
Power cycle the board.
Now we don't have a very good way though...
Maybe some relay or something?
But for now, manually power cycle the board.
"""
input("Please power cycle the board and press enter to continue.")

def get_console(self) -> PyTty:
"""
Get the console of the board.
"""
tty = PySerial(self.serial_port, self.baud)
return tty
73 changes: 73 additions & 0 deletions apps/bpif3_armbian.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""
Gives a configuration,
runs a default procedure,
generates a default log.
"""

from boards.bpif3 import BPiF3
from system.armbian import Armbian
from utils.maintain_img import *
from utils.utils import swap_tty

from tester import *


def default_proc():
"""
Default procedure for testing.
"""

local_shell = PyShell("/bin/bash")
local_shell = PyTee(local_shell, "run.log")
local_shell.write(b"uname -a\n")
local_shell.read()

asciicast = PyAsciicast(local_shell)
asciicast.begin()

e = PyExec(asciicast)

board = BPiF3("id = 0\n", "/dev/ttyUSB0", 115200)

url = "/AArmbian-bpi-SpacemiT_24.5.0-trunk_Bananapif3_mantic_legacy_6.1.15_xfce_desktop.img.xz" # 度盘,dummy
work_dir = "/home/lw/Work/plct/boards/bpif3/armbian"
img = wget_image(url, work_dir)
if img is None:
print("Download failed.")
return
img = extract_image(img)
if img is None:
print("Extract failed.")
return
info(f"Image is ready at {img}")

info("Begin flashing board...")

board.flash(e, img)

info("Flash board ended...")

console = board.get_console()

asciicast = e.exit()
local_shell = swap_tty(asciicast, console)
e = PyExec(asciicast)

board.power_cycle()

info(f"Begin system test...")

system = Armbian(e)

system.loggin()

system.get_info()

asciicast = e.exit()
res = asciicast.end()

with open("res.cast") as f:
f.write(res)

if __name__ == "__main__":
default_proc()
89 changes: 89 additions & 0 deletions apps/system/armbian.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""
For Armbian OS
"""

from time import sleep
from tester import PyExec

def slp(t = 0.5):
"""
Sleep for t second.
"""
sleep(t)

class Armbian:
"""
System class for Armbian.
"""
def __init__(self, tty: PyExec):
self.tty = tty

def wln(self, cmd: str):
"""
Write a command to the board.
"""
self.tty.writeln(cmd)
slp()

def wit(self, wait: str, timeout: int = 600):
"""
Wait for a string.
"""
self.tty.wait_serial(wait, timeout)
slp()

def loggin(self):
"""
Loggin to the board.
"""

# init settings
self.wit("IP address:")
self.wln("")
self.wit("Create root password:")
self.wln("autotest_123")
self.wit("Repeat root password:")
self.wln("autotest_123")
# Choose default system command shell:
slp(2)
self.wln("1")

# register new user
self.wit("(eg. your first name):")
self.wln("plct")
self.wit("password:")
self.wln("plct_123")
self.wit("password:")
self.wln("plct_123")
self.wit("real name:")
self.wln("Plct")

# Set user language based on your location? [Y/n]
self.wit("[Y/n]")
self.wln("y")
# Set time zone
self.wit("choice:")
self.wln("328")
# Please select a continent, ocean, "coord", or "TZ".
self.wit("#?")
self.wln("4")
# Please select a country whose clocks agree with yours.
self.wit("#?")
self.wln("10")
# Set time zone
self.wit("#?")
self.wln("1")
# Confirm time zone
self.wit("#?")
self.wln("1")

# Loggin
self.wit("root@")

def get_info(self):
"""
Get system information.
"""
self.tty.assert_script_run("uname -a")
self.tty.assert_script_run("cat /etc/os-release")
self.tty.assert_script_run("cat /proc/cpuinfo")
89 changes: 89 additions & 0 deletions apps/utils/maintain_img.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""
Some functions to download image from url
Functions:
wget_image(url: str, dir_path: str) -> str
return the path of image file after download
If download failed, return None
If file already exists, return the path of image file
extract_image(file: str) -> str
return the path of image file after extract
If file is not image, return None
For now support .tar, .tar.gz, .tar.bz2, .tar.xz, .zip
"""
import os


def wget_image(url: str, dir_path: str) -> str:
"""
Download image from url and save to dir_path, return the path of image file.
If download failed, return None.
If file already exists, return the path of image file.
"""
# get file name
file_name = url.split('/')[-1]
file_path = os.path.join(dir_path, file_name)
if os.path.exists(file_path):
return file_path

# The image is tend to be REALLY large... So just use wget in shell and wait for it...
os.system(f"wget {url} -O {file_path}")

if os.path.exists(file_path):
return file_path
else:
return None

def extract_image(file: str) -> str:
"""
Extract image from file and return the path of image file.
If file is not image, return None.
"""
# check file type
if not os.path.exists(file):
return None
if not os.path.isfile(file):
return None
# For now support .tar, .tar.gz, .tar.bz2, .tar.xz, .zip
if file.endswith(".tar"):
if os.path.exists(file[:-4]):
return file[:-4]
os.system(f"tar -xf {file}")
return file[:-4]
if file.endswith(".tar.gz"):
if os.path.exists(file[:-7]):
return file[:-7]
os.system(f"tar -xzf {file}")
return file[:-7]
if file.endswith(".tar.bz2"):
if os.path.exists(file[:-8]):
return file[:-8]
os.system(f"tar -xjf {file}")
return file[:-8]
if file.endswith(".tar.xz"):
if os.path.exists(file[:-8]):
return file[:-8]
os.system(f"tar -xJf {file}")
return file[:-8]
if file.endswith(".zip"):
if os.path.exists(file[:-4]):
return file[:-4]
os.system(f"unzip -k {file}")
return file[:-4]
if file.endswith(".xz"):
if os.path.exists(file[:-3]):
return file[:-3]
os.system(f"unxz -k {file}")
return file[:-3]
if file.endswith(".gz"):
if os.path.exists(file[:-3]):
return file[:-3]
os.system(f"gunzip -k {file}")
return file[:-3]
if file.endswith(".bz2"):
if os.path.exists(file[:-4]):
return file[:-4]
os.system(f"bunzip2 -k {file}")
return file[:-4]
return None
9 changes: 9 additions & 0 deletions apps/utils/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
...
"""
def swap_tty(recorder, tty):
"""
...
"""
recorder.swap(tty)
return tty

0 comments on commit c580abb

Please sign in to comment.