Initial commit
Initial commit.
This commit is contained in:
290
bootloader/mcuboot/samples/zephyr/run-tests.go
Normal file
290
bootloader/mcuboot/samples/zephyr/run-tests.go
Normal file
@@ -0,0 +1,290 @@
|
||||
// +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
|
||||
}
|
||||
Reference in New Issue
Block a user