blob: 8124f9f7ca78b4b1939bed4f6c0a53a8c94c98a0 [file] [log] [blame]
David Brown8e0016e2018-01-29 12:15:37 -07001// +build ignore
2//
3// Build multiple configurations of MCUboot for Zephyr, making sure
4// that they run properly.
5//
6// Run as:
7//
8// go run run-tests.go [flags]
9//
10// Add -help as a flag to get help. See comment below for logIn on
11// how to configure terminal output to a file so this program can see
12// the output of the Zephyr device.
13
14package main
15
16import (
17 "bufio"
18 "flag"
19 "fmt"
20 "io"
21 "log"
22 "os"
23 "os/exec"
24 "strings"
25 "time"
David Brown5e8dbb92020-09-09 13:17:38 -060026
27 "github.com/JuulLabs-OSS/mcuboot/samples/zephyr/mcutests"
David Brown8e0016e2018-01-29 12:15:37 -070028)
29
30// logIn gives the pathname of the log output from the Zephyr device.
31// In order to see the serial output, but still be useful for human
32// debugging, the output of the terminal emulator should be teed to a
33// file that this program will read from. This can be done with
34// something like:
35//
36// picocom -b 115200 /dev/ttyACM0 | tee /tmp/zephyr.out
37//
38// Other terminal programs should also have logging options.
39var logIn = flag.String("login", "/tmp/zephyr.out", "File name of terminal log from Zephyr device")
40
41// Output from this test run is written to the given log file.
42var logOut = flag.String("logout", "tests.log", "Log file to write to")
43
David Brownf15a0102020-09-10 08:25:53 -060044var preBuilt = flag.String("prebuilt", "", "Name of file with prebuilt tests")
45
David Brown8e0016e2018-01-29 12:15:37 -070046func main() {
47 err := run()
48 if err != nil {
49 log.Fatal(err)
50 }
51}
52
53func run() error {
54 flag.Parse()
55
56 lines := make(chan string, 30)
57 go readLog(lines)
58
59 // Write output to a log file
60 logFile, err := os.Create(*logOut)
61 if err != nil {
62 return err
63 }
64 defer logFile.Close()
65 lg := bufio.NewWriter(logFile)
66 defer lg.Flush()
67
David Brown5e8dbb92020-09-09 13:17:38 -060068 for _, group := range mcutests.Tests {
69 fmt.Printf("Running %q\n", group.Name)
David Brown8e0016e2018-01-29 12:15:37 -070070 fmt.Fprintf(lg, "-------------------------------------\n")
David Brown5e8dbb92020-09-09 13:17:38 -060071 fmt.Fprintf(lg, "---- Running %q\n", group.Name)
David Brown8e0016e2018-01-29 12:15:37 -070072
David Brown5e8dbb92020-09-09 13:17:38 -060073 for _, test := range group.Tests {
David Brownf15a0102020-09-10 08:25:53 -060074 if *preBuilt == "" {
75 // No prebuilt, build the tests
76 // ourselves.
77 err = runCommands(test.Build, lg)
David Brown8e0016e2018-01-29 12:15:37 -070078 if err != nil {
79 return err
80 }
David Brownf15a0102020-09-10 08:25:53 -060081 } else {
82 panic("TODO")
83 }
84
85 err = runCommands(test.Commands, lg)
86 if err != nil {
87 return err
David Brown8e0016e2018-01-29 12:15:37 -070088 }
89
David Brown5e8dbb92020-09-09 13:17:38 -060090 err = expect(lg, lines, test.Expect)
David Brown8e0016e2018-01-29 12:15:37 -070091 if err != nil {
92 return err
93 }
94
95 fmt.Fprintf(lg, "---- Passed\n")
96 }
97 fmt.Printf(" Passed!\n")
98 }
99
100 return nil
101}
102
David Brownf15a0102020-09-10 08:25:53 -0600103// Run a set of commands
104func runCommands(cmds [][]string, lg io.Writer) error {
105 for _, cmd := range cmds {
106 fmt.Printf(" %s\n", cmd)
107 fmt.Fprintf(lg, "---- Run: %s\n", cmd)
108 err := runCommand(cmd, lg)
109 if err != nil {
110 return err
111 }
112 }
113
114 return nil
115}
116
David Brown8e0016e2018-01-29 12:15:37 -0700117// Run a single command.
118func runCommand(cmd []string, lg io.Writer) error {
119 c := exec.Command(cmd[0], cmd[1:]...)
120 c.Stdout = lg
121 c.Stderr = lg
122 return c.Run()
123}
124
125// Expect the given string.
126func expect(lg io.Writer, lines <-chan string, exp string) error {
127 // Read lines, and if we hit a timeout before seeing our
128 // expected line, then consider that an error.
129 fmt.Fprintf(lg, "---- expect: %q\n", exp)
130
131 stopper := time.NewTimer(10 * time.Second)
132 defer stopper.Stop()
133outer:
134 for {
135 select {
136 case line := <-lines:
137 fmt.Fprintf(lg, "---- target: %q\n", line)
138 if strings.Contains(line, exp) {
139 break outer
140 }
141 case <-stopper.C:
142 fmt.Fprintf(lg, "timeout, didn't receive output\n")
143 return fmt.Errorf("timeout, didn't receive expected string: %q", exp)
144 }
145 }
146
147 return nil
148}
149
150// Read things from the log file, discarding everything already there.
151func readLog(sink chan<- string) {
152 file, err := os.Open(*logIn)
153 if err != nil {
154 log.Fatal(err)
155 }
156
157 _, err = file.Seek(0, 2)
158 if err != nil {
159 log.Fatal(err)
160 }
161
162 prefix := ""
163 for {
164 // Read lines until EOF, then delay a bit, and do it
165 // all again.
166 rd := bufio.NewReader(file)
167
168 for {
169 line, err := rd.ReadString('\n')
170 if err == io.EOF {
171 // A partial line can happen because
172 // we are racing with the writer.
173 if line != "" {
174 prefix = line
175 }
176 break
177 }
178 if err != nil {
179 log.Fatal(err)
180 }
181
182 line = prefix + line
183 prefix = ""
184 sink <- line
185 // fmt.Printf("line: %q\n", line)
186 }
187
188 // Pause a little
189 time.Sleep(250 * time.Millisecond)
190 }
191}