mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-11 22:43:40 +00:00
smoketest: run with go script
This commit is contained in:
@@ -247,8 +247,11 @@ TEST_PACKAGES = \
|
|||||||
tinygo-test:
|
tinygo-test:
|
||||||
$(TINYGO) test $(TEST_PACKAGES)
|
$(TINYGO) test $(TEST_PACKAGES)
|
||||||
|
|
||||||
.PHONY: smoketest
|
.PHONY: smoketest smoketest-commands
|
||||||
smoketest:
|
smoketest:
|
||||||
|
@go run ./src/cmd/run-smoketest make smoketest-commands
|
||||||
|
|
||||||
|
smoketest-commands:
|
||||||
$(TINYGO) version
|
$(TINYGO) version
|
||||||
# test all examples (except pwm)
|
# test all examples (except pwm)
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
||||||
|
|||||||
@@ -0,0 +1,218 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
threads := runtime.NumCPU()
|
||||||
|
if j, ok := os.LookupEnv(`RUN_SMOKETEST_JOBS`); ok {
|
||||||
|
n, err := strconv.ParseInt(j, 0, 0)
|
||||||
|
if err == nil {
|
||||||
|
threads = int(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.IntVar(&threads, "threads", threads, "threads of make smoketest")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
fmt.Printf("usage: %s make smoketest\n", filepath.Base(os.Args[0]))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
commands, err := getMakeCommands(flag.Args())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = run(commands, threads)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMakeCommands(args []string) ([]string, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
cmd.Args = append(cmd.Args, "-n")
|
||||||
|
cmd.Stdout = &buf
|
||||||
|
cmd.Stderr = &buf
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commands := []string{}
|
||||||
|
scanner := bufio.NewScanner(&buf)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if filepath.Base(fields[0]) == "tinygo" && (fields[1] == "build" || fields[1] == "version") {
|
||||||
|
commands = append(commands, line)
|
||||||
|
} else if strings.HasPrefix(line, "#") {
|
||||||
|
commands = append(commands, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(commands []string, threads int) error {
|
||||||
|
ch := make(chan string, 1000)
|
||||||
|
o := NewOchan(ch, 100)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
limit := make(chan struct{}, threads)
|
||||||
|
var eg errgroup.Group
|
||||||
|
|
||||||
|
for _, command := range commands {
|
||||||
|
command := command
|
||||||
|
select {
|
||||||
|
case limit <- struct{}{}:
|
||||||
|
och := o.GetCh()
|
||||||
|
eg.Go(func() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if runtime.GOOS == `windows` {
|
||||||
|
command = convertToWindowsPath(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(command)
|
||||||
|
if filepath.Base(fields[0]) == "tinygo" && fields[1] == "build" {
|
||||||
|
err = buildAndmd5sum(command, och)
|
||||||
|
} else if strings.HasPrefix(command, "#") {
|
||||||
|
och <- fmt.Sprintf("%s\n", command)
|
||||||
|
} else {
|
||||||
|
err = runCommand(command, och)
|
||||||
|
}
|
||||||
|
close(och)
|
||||||
|
<-limit
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Wait()
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for s := range ch {
|
||||||
|
fmt.Print(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCommand(command string, ch chan string) error {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, "%s\n", command)
|
||||||
|
|
||||||
|
args := strings.Fields(command)
|
||||||
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
cmd.Stdout = &buf
|
||||||
|
cmd.Stderr = &buf
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
ch <- buf.String()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- buf.String()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAndmd5sum(command string, ch chan string) error {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, "%s\n", command)
|
||||||
|
|
||||||
|
args := strings.Fields(command)
|
||||||
|
|
||||||
|
output := ""
|
||||||
|
tmpOutput := ""
|
||||||
|
tmpdir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range args {
|
||||||
|
if args[i] == "-o" {
|
||||||
|
output = args[i+1]
|
||||||
|
ext := filepath.Ext(output)
|
||||||
|
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
|
||||||
|
args[i+1] = tmpOutput
|
||||||
|
} else if strings.HasPrefix(args[i], "-o=") {
|
||||||
|
output = args[i][3:]
|
||||||
|
ext := filepath.Ext(output)
|
||||||
|
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
|
||||||
|
args[i] = fmt.Sprintf("-o=%s", tmpOutput)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
cmd.Stdout = &buf
|
||||||
|
cmd.Stderr = &buf
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
ch <- buf.String()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
md5str, err := calcMD5(tmpOutput)
|
||||||
|
if err != nil {
|
||||||
|
ch <- buf.String()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, "%s %s\n", md5str, output)
|
||||||
|
|
||||||
|
ch <- buf.String()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcMD5(f string) (string, error) {
|
||||||
|
r, err := os.Open(f)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
h := md5.New()
|
||||||
|
if _, err := io.Copy(h, r); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToWindowsPath(command string) string {
|
||||||
|
if !strings.HasPrefix(command, `/`) {
|
||||||
|
return command
|
||||||
|
}
|
||||||
|
|
||||||
|
command = fmt.Sprintf("%c:%s", command[1], command[2:])
|
||||||
|
return command
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An Ochan is a structure for controlling the output order of channels.
|
||||||
|
type Ochan struct {
|
||||||
|
out chan string
|
||||||
|
in chan chan string
|
||||||
|
done chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOchan returns a new Ochan struct with specified buffer capacity.
|
||||||
|
func NewOchan(out chan string, size int) *Ochan {
|
||||||
|
o := &Ochan{
|
||||||
|
out: out,
|
||||||
|
in: make(chan chan string, size),
|
||||||
|
done: make(chan struct{}, 1),
|
||||||
|
wg: sync.WaitGroup{},
|
||||||
|
size: size,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(o *Ochan) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ch, ok := <-o.in:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for s := range ch {
|
||||||
|
o.out <- s
|
||||||
|
}
|
||||||
|
o.wg.Done()
|
||||||
|
case <-o.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(o)
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCh returns a next input channel. The input channel must be explicitly
|
||||||
|
// closed after use.
|
||||||
|
func (o *Ochan) GetCh() chan string {
|
||||||
|
ch := make(chan string, o.size)
|
||||||
|
o.in <- ch
|
||||||
|
o.wg.Add(1)
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize sets the capacity of the channel returned by GetCh.
|
||||||
|
func (o *Ochan) SetSize(size int) {
|
||||||
|
o.size = size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait blocks until it retrieves data from all input channel. All input
|
||||||
|
// channels must be closed before calling this function.
|
||||||
|
func (o *Ochan) Wait() error {
|
||||||
|
o.wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closed ochan's goroutine.
|
||||||
|
func (o *Ochan) Close() {
|
||||||
|
close(o.done)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user