291 lines
5.9 KiB
Go
291 lines
5.9 KiB
Go
// +build ignore
|
|
//
|
|
// Build multiple configurations of MCUboot for Zephyr, making sure
|
|
// that they run properly.
|
|
//
|
|
// Run as:
|
|
//
|
|
// go run run-tests.go [flags]
|
|
//
|
|
// Add -help as a flag to get help. See comment below for logIn on
|
|
// how to configure terminal output to a file so this program can see
|
|
// the output of the Zephyr device.
|
|
|
|
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mcu-tools/mcuboot/samples/zephyr/mcutests"
|
|
)
|
|
|
|
// logIn gives the pathname of the log output from the Zephyr device.
|
|
// In order to see the serial output, but still be useful for human
|
|
// debugging, the output of the terminal emulator should be teed to a
|
|
// file that this program will read from. This can be done with
|
|
// something like:
|
|
//
|
|
// picocom -b 115200 /dev/ttyACM0 | tee /tmp/zephyr.out
|
|
//
|
|
// Other terminal programs should also have logging options.
|
|
var logIn = flag.String("login", "/tmp/zephyr.out", "File name of terminal log from Zephyr device")
|
|
|
|
// Output from this test run is written to the given log file.
|
|
var logOut = flag.String("logout", "tests.log", "Log file to write to")
|
|
|
|
var preBuilt = flag.String("prebuilt", "", "Name of file with prebuilt tests")
|
|
|
|
func main() {
|
|
err := run()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func run() error {
|
|
flag.Parse()
|
|
|
|
lines := make(chan string, 30)
|
|
go readLog(lines)
|
|
|
|
// Write output to a log file
|
|
logFile, err := os.Create(*logOut)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer logFile.Close()
|
|
lg := bufio.NewWriter(logFile)
|
|
defer lg.Flush()
|
|
|
|
var extractor *Extractor
|
|
|
|
if *preBuilt != "" {
|
|
// If there are pre-built images, open them.
|
|
extractor, err = NewExtractor(*preBuilt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer extractor.Close()
|
|
}
|
|
|
|
for _, group := range mcutests.Tests {
|
|
fmt.Printf("Running %q\n", group.Name)
|
|
fmt.Fprintf(lg, "-------------------------------------\n")
|
|
fmt.Fprintf(lg, "---- Running %q\n", group.Name)
|
|
|
|
for _, test := range group.Tests {
|
|
if *preBuilt == "" {
|
|
// No prebuilt, build the tests
|
|
// ourselves.
|
|
err = runCommands(test.Build, lg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// Extract the build artifacts from
|
|
// the zip file.
|
|
err = extractor.Extract(group.ShortName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = runCommands(test.Commands, lg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = expect(lg, lines, test.Expect)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprintf(lg, "---- Passed\n")
|
|
}
|
|
fmt.Printf(" Passed!\n")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Run a set of commands
|
|
func runCommands(cmds [][]string, lg io.Writer) error {
|
|
for _, cmd := range cmds {
|
|
fmt.Printf(" %s\n", cmd)
|
|
fmt.Fprintf(lg, "---- Run: %s\n", cmd)
|
|
err := runCommand(cmd, lg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Run a single command.
|
|
func runCommand(cmd []string, lg io.Writer) error {
|
|
c := exec.Command(cmd[0], cmd[1:]...)
|
|
c.Stdout = lg
|
|
c.Stderr = lg
|
|
return c.Run()
|
|
}
|
|
|
|
// Expect the given string.
|
|
func expect(lg io.Writer, lines <-chan string, exp string) error {
|
|
// Read lines, and if we hit a timeout before seeing our
|
|
// expected line, then consider that an error.
|
|
fmt.Fprintf(lg, "---- expect: %q\n", exp)
|
|
|
|
stopper := time.NewTimer(10 * time.Second)
|
|
defer stopper.Stop()
|
|
outer:
|
|
for {
|
|
select {
|
|
case line := <-lines:
|
|
fmt.Fprintf(lg, "---- target: %q\n", line)
|
|
if strings.Contains(line, exp) {
|
|
break outer
|
|
}
|
|
case <-stopper.C:
|
|
fmt.Fprintf(lg, "timeout, didn't receive output\n")
|
|
return fmt.Errorf("timeout, didn't receive expected string: %q", exp)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Read things from the log file, discarding everything already there.
|
|
func readLog(sink chan<- string) {
|
|
file, err := os.Open(*logIn)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
_, err = file.Seek(0, 2)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
prefix := ""
|
|
for {
|
|
// Read lines until EOF, then delay a bit, and do it
|
|
// all again.
|
|
rd := bufio.NewReader(file)
|
|
|
|
for {
|
|
line, err := rd.ReadString('\n')
|
|
if err == io.EOF {
|
|
// A partial line can happen because
|
|
// we are racing with the writer.
|
|
if line != "" {
|
|
prefix = line
|
|
}
|
|
break
|
|
}
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
line = prefix + line
|
|
prefix = ""
|
|
sink <- line
|
|
// fmt.Printf("line: %q\n", line)
|
|
}
|
|
|
|
// Pause a little
|
|
time.Sleep(250 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
// An Extractor holds an opened Zip file, and is able to extract files
|
|
// based on the directory name.
|
|
type Extractor struct {
|
|
file *os.File
|
|
zip *zip.Reader
|
|
}
|
|
|
|
// NewExtractor returns an Extractor based on the contents of a zip
|
|
// file.
|
|
func NewExtractor(name string) (*Extractor, error) {
|
|
f, err := os.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
size, err := f.Seek(0, 2)
|
|
if err != nil {
|
|
f.Close()
|
|
return nil, err
|
|
}
|
|
|
|
rd, err := zip.NewReader(f, size)
|
|
if err != nil {
|
|
f.Close()
|
|
return nil, err
|
|
}
|
|
|
|
return &Extractor{
|
|
file: f,
|
|
zip: rd,
|
|
}, nil
|
|
}
|
|
|
|
func (e *Extractor) Close() error {
|
|
return e.file.Close()
|
|
}
|
|
|
|
// Extract extracts the files of the given directory name into the
|
|
// current directory. These files will overwrite any files of these
|
|
// names that already exist (presumably from previous extractions).
|
|
func (e *Extractor) Extract(dir string) error {
|
|
prefix := dir + "/"
|
|
|
|
count := 0
|
|
for _, file := range e.zip.File {
|
|
if len(file.Name) > len(prefix) && strings.HasPrefix(file.Name, prefix) {
|
|
outName := file.Name[len(prefix):len(file.Name)]
|
|
fmt.Printf("->%q\n", outName)
|
|
|
|
err := e.single(file, outName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
count += 1
|
|
}
|
|
}
|
|
|
|
if count == 0 {
|
|
return fmt.Errorf("File for %s missing from archive", dir)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// single extracts a single file from the zip archive, writing the
|
|
// results to a file 'outName'.
|
|
func (e *Extractor) single(file *zip.File, outName string) error {
|
|
inf, err := file.Open()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
outf, err := os.Create(outName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outf.Close()
|
|
|
|
_, err = io.Copy(outf, inf)
|
|
return err
|
|
}
|