fspl/compiler/common_test.go

161 lines
4.2 KiB
Go

package compiler
import "time"
import "io/fs"
import "embed"
import "strings"
import "testing"
import "context"
import "os/exec"
import "path/filepath"
import "git.tebibyte.media/fspl/fspl/entity"
import "git.tebibyte.media/fspl/fspl/errors"
import "git.tebibyte.media/fspl/fspl/testcommon"
import "git.tebibyte.media/fspl/fspl/generator/native"
//go:embed all:test-data/*
var testData embed.FS
func defaultCompiler () *Compiler {
// instantiate and configure the compiler
comp := new(Compiler)
comp.Prefix = "compiler"
comp.Resolver = NewResolver (
"/test-data/usr/local/src/fspl",
"/test-data/usr/src/fspl",
"/test-data/usr/local/incude/fspl",
"/test-data/usr/include/fspl")
comp.Resolver.FS = testData
comp.Filetype = FiletypeObject
comp.Debug = true
nativeTarget := native.NativeTarget()
comp.Target = &nativeTarget
return comp
}
func compileDependency (
test *testing.T,
address entity.Address,
) string {
// create temporary directory
temp := test.TempDir()
// instantiate and configure the compiler
compOutputBuilder := new(strings.Builder)
comp := defaultCompiler()
defer func () {
test.Logf (
"COMPILER LOG (dependency %s):\n%s",
address, compOutputBuilder)
} ()
comp.Writer = compOutputBuilder
nickname, ok := address.Nickname()
if !ok {
test.Fatal("could not generate nickname for", address)
}
comp.Output = filepath.Join(temp, FiletypeObject.Extend(*comp.Target, nickname))
// compile to object file
err := comp.CompileUnit(address)
if err != nil {
test.Fatal("compiler returned error:", errors.Format(err))
}
return comp.Output
}
func testUnit (
test *testing.T,
address entity.Address,
clangArgs []string,
stdin, stdout string,
exit int,
args ...string,
) {
// create temporary directory
temp := test.TempDir()
// instantiate and configure the compiler
compOutputBuilder := new(strings.Builder)
comp := defaultCompiler()
outputCompilerLog := func () {
test.Log("COMPILER LOG (main unit):\n" + compOutputBuilder.String())
}
comp.Writer = compOutputBuilder
comp.Output = filepath.Join(temp, FiletypeObject.Extend(*comp.Target, "output"))
// compile to object file
err := comp.CompileUnit(address)
if err != nil {
outputCompilerLog()
test.Fatal("compiler returned error:", errors.Format(err))
}
outputCompilerLog()
// link the object file into an executable
linkOutputBuilder := new(strings.Builder)
executablePath := FiletypeExecutable.Extend(*comp.Target, filepath.Join(temp, "output"))
linkCommand := exec.Command("clang", append (
clangArgs,
comp.Output,
"-v",
"-o",
executablePath)...)
linkCommand.Stdout = linkOutputBuilder
linkCommand.Stderr = linkOutputBuilder
test.Log("running link command: ", linkCommand)
err = linkCommand.Run()
test.Log("LINKER LOG (main unit):\n" + linkOutputBuilder.String())
if err != nil {
test.Fatal("error linking executable:", err)
}
// run the executable file (with timeout) and check its output
timeoutDuration := 4000 * time.Millisecond
ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration)
defer cancel()
executableCommand := exec.CommandContext(ctx, executablePath, args...)
stdoutBuilder := new(strings.Builder)
executableCommand.Stdin = strings.NewReader(stdin)
executableCommand.Stdout = stdoutBuilder
test.Log("running executable command: ", executableCommand)
err = executableCommand.Run()
if ctx.Err() == context.DeadlineExceeded {
test.Errorf("timeout of %v exceeded!", timeoutDuration)
}
// check error, compare exit code
if exitErr, ok := err.(*exec.ExitError); ok {
code := exitErr.ExitCode()
if code != exit {
test.Errorf (
"expecting exit code %d, got %d",
exit, code)
}
} else if err != nil {
test.Fatalf("error running %s: %v", executablePath, err)
} else {
if 0 != exit {
test.Errorf("expecting exit code %d, got 0", exit)
}
}
// compare stdout
gotStdout := stdoutBuilder.String()
if gotStdout != stdout {
testcommon.CompareHex(test, stdout, gotStdout)
test.Fail()
}
}
func TestEmbedOk (test *testing.T) {
err := fs.WalkDir(testData, ".", func (path string, d fs.DirEntry, err error) error {
test.Log("file:", path)
return err
})
if err != nil {
test.Error("walk dir failed: ", err)
}
}