Skip to content

Commit 0b6970f

Browse files
committed
Add test command to tinygo
* Test command passes flag to compile indicating that a test binary should be built * Adds the test build tag * Imports packages from test files Stuck on how to find the TestMain that I have defined in my test file. It is coming back nil. Not sure how I would reflect on the functions defined in the test files and then build up the test suite (M) in a basic block. First step I figured was calling a simple predefined test suite though.
1 parent 225e228 commit 0b6970f

File tree

9 files changed

+167
-52
lines changed

9 files changed

+167
-52
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ fmt:
8787

8888
test:
8989
@go test -v .
90+
$(MAKE) tinygo-test
91+
92+
tinygo-test: build/tinygo
93+
cd tests/example1 && ../../build/tinygo test
9094

9195
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-stm32
9296

compiler/compiler.go

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,24 @@ func init() {
3030

3131
// Configure the compiler.
3232
type Config struct {
33-
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
34-
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
35-
GOOS string //
36-
GOARCH string //
37-
GC string // garbage collection strategy
38-
CFlags []string // cflags to pass to cgo
39-
LDFlags []string // ldflags to pass to cgo
40-
DumpSSA bool // dump Go SSA, for compiler debugging
41-
Debug bool // add debug symbols for gdb
42-
RootDir string // GOROOT for TinyGo
43-
GOPATH string // GOPATH, like `go env GOPATH`
44-
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
33+
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
34+
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
35+
GOOS string //
36+
GOARCH string //
37+
GC string // garbage collection strategy
38+
CFlags []string // cflags to pass to cgo
39+
LDFlags []string // ldflags to pass to cgo
40+
DumpSSA bool // dump Go SSA, for compiler debugging
41+
Debug bool // add debug symbols for gdb
42+
RootDir string // GOROOT for TinyGo
43+
GOPATH string // GOPATH, like `go env GOPATH`
44+
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
45+
TestConfig TestConfig
46+
}
47+
48+
type TestConfig struct {
49+
CompileTestBinary bool
50+
// TODO: Filter the test functions to run, include verbose flag, etc
4551
}
4652

4753
type Compiler struct {
@@ -205,12 +211,13 @@ func (c *Compiler) Compile(mainPath string) error {
205211
return err
206212
}
207213
}
214+
208215
_, err = lprogram.Import("runtime", "")
209216
if err != nil {
210217
return err
211218
}
212219

213-
err = lprogram.Parse()
220+
err = lprogram.Parse(c.TestConfig.CompileTestBinary)
214221
if err != nil {
215222
return err
216223
}
@@ -332,9 +339,16 @@ func (c *Compiler) Compile(mainPath string) error {
332339
}
333340
c.builder.CreateRetVoid()
334341

342+
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
343+
344+
// Swap the main impl with the TestMain block
345+
// TODO: generate a TestMain
346+
//l := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".TestMain")
347+
// TODO: why is this nil?
348+
//fmt.Println("DEBUG TestMain: ", l)
349+
335350
// Conserve for goroutine lowering. Without marking these as external, they
336351
// would be optimized away.
337-
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
338352
realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering
339353
c.mod.NamedFunction("runtime.alloc").SetLinkage(llvm.ExternalLinkage)
340354
c.mod.NamedFunction("runtime.free").SetLinkage(llvm.ExternalLinkage)

loader/loader.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
5353
p.sorted = nil // invalidate the sorted order of packages
5454
pkg := p.newPackage(buildPkg)
5555
p.Packages[buildPkg.ImportPath] = pkg
56+
5657
return pkg, nil
5758
}
5859

@@ -160,10 +161,10 @@ func (p *Program) sort() {
160161
// The returned error may be an Errors error, which contains a list of errors.
161162
//
162163
// Idempotent.
163-
func (p *Program) Parse() error {
164+
func (p *Program) Parse(includeTests bool) error {
164165
// Load all imports
165166
for _, pkg := range p.Sorted() {
166-
err := pkg.importRecursively()
167+
err := pkg.importRecursively(includeTests)
167168
if err != nil {
168169
if err, ok := err.(*ImportCycleError); ok {
169170
if pkg.ImportPath != err.Packages[0] {
@@ -176,7 +177,7 @@ func (p *Program) Parse() error {
176177

177178
// Parse all packages.
178179
for _, pkg := range p.Sorted() {
179-
err := pkg.Parse()
180+
err := pkg.Parse(includeTests)
180181
if err != nil {
181182
return err
182183
}
@@ -217,7 +218,7 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
217218
// Parse parses and typechecks this package.
218219
//
219220
// Idempotent.
220-
func (p *Package) Parse() error {
221+
func (p *Package) Parse(includeTests bool) error {
221222
if len(p.Files) != 0 {
222223
return nil
223224
}
@@ -231,7 +232,7 @@ func (p *Package) Parse() error {
231232
return nil
232233
}
233234

234-
files, err := p.parseFiles()
235+
files, err := p.parseFiles(includeTests)
235236
if err != nil {
236237
return err
237238
}
@@ -270,11 +271,21 @@ func (p *Package) Check() error {
270271
}
271272

272273
// parseFiles parses the loaded list of files and returns this list.
273-
func (p *Package) parseFiles() ([]*ast.File, error) {
274+
func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
274275
// TODO: do this concurrently.
275276
var files []*ast.File
276277
var fileErrs []error
277-
for _, file := range p.GoFiles {
278+
279+
var gofiles []string
280+
if includeTests {
281+
gofiles = make([]string, 0, len(p.GoFiles)+len(p.TestGoFiles))
282+
gofiles = append(gofiles, p.GoFiles...)
283+
gofiles = append(gofiles, p.TestGoFiles...)
284+
} else {
285+
gofiles = p.GoFiles
286+
}
287+
288+
for _, file := range gofiles {
278289
f, err := p.parseFile(filepath.Join(p.Package.Dir, file), parser.ParseComments)
279290
if err != nil {
280291
fileErrs = append(fileErrs, err)
@@ -323,9 +334,15 @@ func (p *Package) Import(to string) (*types.Package, error) {
323334
// importRecursively() on the imported packages as well.
324335
//
325336
// Idempotent.
326-
func (p *Package) importRecursively() error {
337+
func (p *Package) importRecursively(includeTests bool) error {
327338
p.Importing = true
328-
for _, to := range p.Package.Imports {
339+
340+
imports := p.Package.Imports
341+
if includeTests {
342+
imports = append(imports, p.Package.TestImports...)
343+
}
344+
345+
for _, to := range imports {
329346
if to == "C" {
330347
// Do Cgo processing in a later stage.
331348
continue
@@ -343,7 +360,7 @@ func (p *Package) importRecursively() error {
343360
if importedPkg.Importing {
344361
return &ImportCycleError{[]string{p.ImportPath, importedPkg.ImportPath}, p.ImportPos[to]}
345362
}
346-
err = importedPkg.importRecursively()
363+
err = importedPkg.importRecursively(includeTests)
347364
if err != nil {
348365
if err, ok := err.(*ImportCycleError); ok {
349366
err.Packages = append([]string{p.ImportPath}, err.Packages...)

main.go

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type BuildConfig struct {
5050
cFlags []string
5151
ldFlags []string
5252
wasmAbi string
53+
testConfig compiler.TestConfig
5354
}
5455

5556
// Helper function for Compiler object.
@@ -63,18 +64,19 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
6364
spec.LDFlags = append(spec.LDFlags, config.ldFlags...)
6465

6566
compilerConfig := compiler.Config{
66-
Triple: spec.Triple,
67-
CPU: spec.CPU,
68-
GOOS: spec.GOOS,
69-
GOARCH: spec.GOARCH,
70-
GC: config.gc,
71-
CFlags: spec.CFlags,
72-
LDFlags: spec.LDFlags,
73-
Debug: config.debug,
74-
DumpSSA: config.dumpSSA,
75-
RootDir: sourceDir(),
76-
GOPATH: getGopath(),
77-
BuildTags: spec.BuildTags,
67+
Triple: spec.Triple,
68+
CPU: spec.CPU,
69+
GOOS: spec.GOOS,
70+
GOARCH: spec.GOARCH,
71+
GC: config.gc,
72+
CFlags: spec.CFlags,
73+
LDFlags: spec.LDFlags,
74+
Debug: config.debug,
75+
DumpSSA: config.dumpSSA,
76+
RootDir: sourceDir(),
77+
GOPATH: getGopath(),
78+
BuildTags: spec.BuildTags,
79+
TestConfig: config.testConfig,
7880
}
7981
c, err := compiler.NewCompiler(pkgName, compilerConfig)
8082
if err != nil {
@@ -312,6 +314,30 @@ func Build(pkgName, outpath, target string, config *BuildConfig) error {
312314
})
313315
}
314316

317+
func Test(pkgName, target string, config *BuildConfig) error {
318+
spec, err := LoadTarget(target)
319+
if err != nil {
320+
return err
321+
}
322+
323+
spec.BuildTags = append(spec.BuildTags, "test")
324+
config.testConfig.CompileTestBinary = true
325+
return Compile(pkgName, ".elf", spec, config, func(tmppath string) error {
326+
cmd := exec.Command(tmppath)
327+
cmd.Stdout = os.Stdout
328+
cmd.Stderr = os.Stderr
329+
err := cmd.Run()
330+
if err != nil {
331+
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
332+
// Workaround for QEMU which always exits with an error.
333+
return nil
334+
}
335+
return &commandError{"failed to run compiled binary", tmppath, err}
336+
}
337+
return nil
338+
})
339+
}
340+
315341
func Flash(pkgName, target, port string, config *BuildConfig) error {
316342
spec, err := LoadTarget(target)
317343
if err != nil {
@@ -608,8 +634,13 @@ func main() {
608634
err := Run(flag.Arg(0), *target, config)
609635
handleCompilerError(err)
610636
case "test":
611-
RunTests()
612-
os.Exit(0)
637+
pkgRoot, err := getPackageRoot()
638+
if err != nil {
639+
fmt.Fprintln(os.Stderr, err)
640+
os.Exit(1)
641+
}
642+
err = Test(pkgRoot, *target, config)
643+
handleCompilerError(err)
613644
case "clean":
614645
// remove cache directory
615646
dir := cacheDir()

src/os/proc.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package os
2+
3+
import (
4+
"syscall"
5+
)
6+
7+
// Exit the current program with the specified status code.
8+
func Exit(code int) {
9+
// TODO: This doesn't work, not sure what needs to be implemented to make it work?
10+
syscall.Exit(code)
11+
}

src/reflect/value.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func (v Value) Pointer() uintptr {
9292
}
9393

9494
func (v Value) IsValid() bool {
95-
panic("unimplemented: (reflect.Value).IsValid()")
95+
return v.typecode != 0
9696
}
9797

9898
func (v Value) CanInterface() bool {
@@ -335,7 +335,7 @@ func (v Value) Index(i int) Value {
335335
typecode: v.Type().Elem(),
336336
indirect: true,
337337
}
338-
addr := uintptr(slice.Data) + elem.Type().Size() * uintptr(i) // pointer to new value
338+
addr := uintptr(slice.Data) + elem.Type().Size()*uintptr(i) // pointer to new value
339339
elem.value = unsafe.Pointer(addr)
340340
return elem
341341
case String:
@@ -348,7 +348,7 @@ func (v Value) Index(i int) Value {
348348
}
349349
return Value{
350350
typecode: Uint8.basicType(),
351-
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(s.Data + uintptr(i))))),
351+
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(s.Data + uintptr(i))))),
352352
}
353353
case Array:
354354
panic("unimplemented: (reflect.Value).Index()")

src/testing/doc.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package testing
2+
3+
/*
4+
This is a sad stub of the upstream testing package because it doesn't compile
5+
with tinygo right now.
6+
*/

src/testing/testing.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,51 @@ import (
44
"fmt"
55
)
66

7+
// T is a test helper.
78
type T struct {
9+
}
10+
11+
// TestToCall is a reference to a test that should be called during a test suite run.
12+
type TestToCall struct {
13+
// Name of the test to call.
14+
Name string
15+
// Function reference to the test.
16+
Func func(*T)
17+
}
18+
19+
// M is a test suite.
20+
type M struct {
21+
// tests is a list of the test names to execute
22+
Tests []TestToCall
23+
}
824

25+
// Run the test suite.
26+
func (m *M) Run() int {
27+
for _, test := range m.Tests {
28+
t := &T{}
29+
test.Func(t)
30+
}
31+
// TODO: detect failures and return a failing exit code
32+
// Right now we can't handle one anyway so it doesn't matter much
33+
return 0
934
}
1035

36+
// Fatal is equivalent to Log followed by FailNow
1137
func (t *T) Fatal(args ...string) {
12-
fmt.Println(args)
13-
}
38+
// This doesn't print the same as in upstream go, but works good enough
39+
fmt.Print(args)
40+
//t.FailNow()
41+
}
42+
43+
/*
44+
func (t *T) FailNow() {
45+
// This fails with
46+
Undefined symbols for architecture x86_64:
47+
"_syscall.Exit", referenced from:
48+
_main in main.o
49+
ld: symbol(s) not found for architecture x86_64
50+
clang: error: linker command failed with exit code 1 (use -v to see invocation)
51+
error: failed to link /var/folders/_t/fbw4wf_s42dfqdfjq7shdfm40000gn/T/tinygo382510652/main: exit status 1
52+
os.Exit(12)
53+
}
54+
*/

src/testing/testmain.go

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)