Слияние кода завершено, страница обновится автоматически
// +build integration
package main
import (
"bytes"
"flag"
"fmt"
"go/format"
"go/parser"
"go/token"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"syscall"
"testing"
"github.com/elliotchance/c2go/cc"
"github.com/elliotchance/c2go/util"
)
type programOut struct {
stdout bytes.Buffer
stderr bytes.Buffer
isZero bool
}
// TestIntegrationScripts tests all programs in the tests directory.
//
// Integration tests are not run by default (only unit tests). These are
// indicated by the build flags at the top of the file. To include integration
// tests use:
//
// go test -tags=integration
//
// You can also run a single file with:
//
// go test -tags=integration -run=TestIntegrationScripts/tests/ctype.c
//
func TestIntegrationScripts(t *testing.T) {
testFiles, err := filepath.Glob("tests/*.c")
if err != nil {
t.Fatal(err)
}
exampleFiles, err := filepath.Glob("examples/*.c")
if err != nil {
t.Fatal(err)
}
files := append(testFiles, exampleFiles...)
isVerbose := flag.CommandLine.Lookup("test.v").Value.String() == "true"
totalTapTests := 0
var (
buildFolder = "build"
cFileName = "a.out"
mainFileName = "main.go"
stdin = "7"
args = []string{"some", "args"}
separator = string(os.PathSeparator)
)
// Parallel is not acceptable, before solving issue:
// https://github.com/elliotchance/c2go/issues/376
// t.Parallel()
for _, file := range files {
t.Run(file, func(t *testing.T) {
cc.ResetCache()
cProgram := programOut{}
goProgram := programOut{}
// create sub folders for test
subFolder := buildFolder + separator + strings.Split(file, ".")[0] + separator
cPath := subFolder + cFileName
// Create build folder
err = os.MkdirAll(subFolder, os.ModePerm)
if err != nil {
t.Fatalf("error: %v", err)
}
// Compile C.
out, err := exec.Command("clang", "-lm", "-o", cPath, file).CombinedOutput()
if err != nil {
t.Fatalf("error: %s\n%s", err, out)
}
// Run C program
cmd := exec.Command(cPath, args...)
cmd.Stdin = strings.NewReader(stdin)
cmd.Stdout = &cProgram.stdout
cmd.Stderr = &cProgram.stderr
err = cmd.Run()
cProgram.isZero = err == nil
// Check for special exit codes that signal that tests have failed.
if exitError, ok := err.(*exec.ExitError); ok {
exitStatus := exitError.Sys().(syscall.WaitStatus).ExitStatus()
if exitStatus == 101 || exitStatus == 102 {
t.Fatal(cProgram.stdout.String())
}
}
mainFileName = "main_test.go"
programArgs := DefaultProgramArgs()
programArgs.inputFiles = []string{file}
programArgs.outputFile = subFolder + mainFileName
// This appends a TestApp function to the output source so we
// can run "go test" against the produced binary.
programArgs.outputAsTest = true
// Compile Go
err = Start(programArgs)
if err != nil {
t.Fatalf("error: %s\n%s", err, out)
}
// Run Go program. The "-v" option is important; without it most or
// all of the fmt.* output would be suppressed.
args := []string{
"test",
programArgs.outputFile,
"-v",
}
if strings.Index(file, "examples/") == -1 {
testName := strings.Split(file, ".")[0][6:]
args = append(
args,
"-race",
"-covermode=atomic",
"-coverprofile="+testName+".coverprofile",
"-coverpkg=./noarch,./linux,./darwin",
)
}
args = append(args, "--", "some", "args")
cmd = exec.Command("go", args...)
cmd.Stdin = strings.NewReader("7")
cmd.Stdout = &goProgram.stdout
cmd.Stderr = &goProgram.stderr
err = cmd.Run()
goProgram.isZero = err == nil
// Check stderr. "go test" will produce warnings when packages are
// not referenced as dependencies. We need to strip out these
// warnings so it doesn't effect the comparison.
cProgramStderr := cProgram.stderr.String()
goProgramStderr := goProgram.stderr.String()
r := util.GetRegex("warning: no packages being tested depend on .+\n")
goProgramStderr = r.ReplaceAllString(goProgramStderr, "")
// It is need only for "tests/assert.c"
// for change absolute path to local path
currentDir, err := os.Getwd()
if err != nil {
t.Fatal("Cannot get currently dir")
}
goProgramStderr = strings.Replace(goProgramStderr, currentDir+"/", "", -1)
if cProgramStderr != goProgramStderr {
// Add addition debug information for lines like:
// build/tests/cast/main_test.go:195:1: expected '}', found 'type'
buildPrefix := "build/tests/"
var output string
lines := strings.Split(goProgramStderr, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if !strings.HasPrefix(line, buildPrefix) {
continue
}
index := strings.Index(line, ":")
if index < 0 {
continue
}
filename := "./" + line[0:index]
output += "+========================+\n"
output += fmt.Sprintf("File : %s\n\n", filename)
if len(line) <= index+1 {
continue
}
line = line[index+1:]
index = strings.Index(line, ":")
if index < 0 {
continue
}
linePosition, err := strconv.Atoi(line[:index])
if err != nil {
err = nil
continue
}
content, err := ioutil.ReadFile(filename)
if err != nil {
err = nil
continue
}
fileLines := strings.Split(string(content), "\n")
start := linePosition - 20
if start < 0 {
start = 0
}
for i := start; i < linePosition+5 && i < len(fileLines); i++ {
output += fmt.Sprintf("Line : %3d : %s\n", i, fileLines[i])
}
}
t.Fatalf("Expected %s\nGot: %s\nParts of code:\n%s",
cProgramStderr, goProgramStderr, output)
}
// Check stdout
cOut := cProgram.stdout.String()
goOutLines := strings.Split(goProgram.stdout.String(), "\n")
// An out put should look like this:
//
// === RUN TestApp
// 1..3
// 1 ok - argc == 3 + offset
// 2 ok - argv[1 + offset] == "some"
// 3 ok - argv[2 + offset] == "args"
// --- PASS: TestApp (0.03s)
// PASS
// coverage: 0.0% of statements
// ok command-line-arguments 1.050s
//
// The first line and 4 of the last lines can be ignored as they are
// part of the "go test" runner and not part of the program output.
//
// Note: There is a blank line at the end of the output so when we
// say the last line we are really talking about the second last
// line. Rather than trimming the whitespace off the C and Go output
// we will just make note of the different line index.
//
// Some tests are designed to fail, like assert.c. In this case the
// result output is slightly different:
//
// === RUN TestApp
// 1..0
// 10
// # FAILED: There was 1 failed tests.
// exit status 101
// FAIL command-line-arguments 0.041s
//
// The last three lines need to be removed.
//
// Before we proceed comparing the raw output we should check that
// the header and footer of the output fits one of the two formats
// in the examples above.
if goOutLines[0] != "=== RUN TestApp" {
t.Fatalf("The header of the output cannot be understood:\n%s",
strings.Join(goOutLines, "\n"))
}
if !strings.HasPrefix(goOutLines[len(goOutLines)-2], "ok \tcommand-line-arguments") &&
!strings.HasPrefix(goOutLines[len(goOutLines)-2], "FAIL\tcommand-line-arguments") {
t.Fatalf("The footer of the output cannot be understood:\n%v",
strings.Join(goOutLines, "\n"))
}
// A failure will cause (always?) "go test" to output the exit code
// before the final line. We should also ignore this as its not part
// of our output.
//
// There is a separate check to see that both the C and Go programs
// return the same exit code.
removeLinesFromEnd := 5
if strings.Index(file, "examples/") >= 0 {
removeLinesFromEnd = 4
} else if strings.HasPrefix(goOutLines[len(goOutLines)-3], "exit status") {
removeLinesFromEnd = 3
}
var goOut string
if len(goOutLines)-removeLinesFromEnd < 1 {
goOut = "\n"
} else {
goOut = strings.Join(goOutLines[1:len(goOutLines)-removeLinesFromEnd], "\n") + "\n"
}
// Check if both exit codes are zero (or non-zero)
if cProgram.isZero != goProgram.isZero {
t.Fatalf("Exit statuses did not match.\n%s", util.ShowDiff(cOut, goOut))
}
if cOut != goOut {
t.Fatalf(util.ShowDiff(cOut, goOut))
}
// If this is not an example we will extract the number of tests
// run.
if strings.Index(file, "examples/") == -1 && isVerbose {
firstLine := strings.Split(goOut, "\n")[0]
matches := util.GetRegex(`1\.\.(\d+)`).
FindStringSubmatch(firstLine)
if len(matches) == 0 {
t.Fatalf("Test did not output tap: %s, got:\n%s", file,
goProgram.stdout.String())
}
fmt.Printf("TAP: # %s: %s tests\n", file, matches[1])
totalTapTests += util.Atoi(matches[1])
}
})
}
if isVerbose {
fmt.Printf("TAP: # Total tests: %d\n", totalTapTests)
}
}
func TestStartPreprocess(t *testing.T) {
// create temp file with guarantee
// wrong file body
dir, err := ioutil.TempDir("", "c2go-preprocess")
if err != nil {
t.Fatalf("Cannot create temp folder: %v", err)
}
defer os.RemoveAll(dir) // clean up
name := "preprocess.c"
filename := path.Join(dir, name)
body := ([]byte)("#include <AbsoluteWrongInclude.h>\nint main(void){\nwrong();\n}")
err = ioutil.WriteFile(filename, body, 0644)
args := DefaultProgramArgs()
args.inputFiles = []string{dir + name}
err = Start(args)
if err == nil {
t.Fatalf("Cannot test preprocess of application")
}
}
func TestMultiFileTranspilation(t *testing.T) {
tcs := []struct {
source []string
expectedOutput string
}{
{
[]string{
"./tests/multi/main1.c",
"./tests/multi/main2.c",
},
"234ERROR!ERROR!ERROR!",
},
{
[]string{
"./tests/multi-struct/main.c",
"./tests/multi-struct/types.c",
},
"42\n",
},
}
for pos, tc := range tcs {
t.Run(fmt.Sprintf("Test %d", pos), func(t *testing.T) {
var args = DefaultProgramArgs()
args.inputFiles = tc.source
dir, err := ioutil.TempDir("", "c2go_multi")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // clean up
args.outputFile = path.Join(dir, "multi.go")
args.packageName = "main"
args.outputAsTest = true
// testing
err = Start(args)
if err != nil {
t.Errorf(err.Error())
}
// Run Go program
var buf bytes.Buffer
cmd := exec.Command("go", "run", args.outputFile)
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
if err != nil {
t.Errorf(err.Error())
}
if buf.String() != tc.expectedOutput {
t.Errorf("Wrong result: %v", buf.String())
}
})
}
}
func TestTriGraph(t *testing.T) {
var args = DefaultProgramArgs()
args.inputFiles = []string{"./tests/trigraph/main.c"}
dir, err := ioutil.TempDir("", "c2go_trigraph")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // clean up
args.outputFile = path.Join(dir, "multi.go")
args.clangFlags = []string{"-trigraphs"}
args.packageName = "main"
args.outputAsTest = true
// testing
err = Start(args)
if err != nil {
t.Errorf(err.Error())
}
// Run Go program
var buf bytes.Buffer
cmd := exec.Command("go", "run", args.outputFile)
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
if err != nil {
t.Errorf(err.Error())
}
if buf.String() != "#" {
t.Errorf("Wrong result: %v", buf.String())
}
}
func TestExternalInclude(t *testing.T) {
var args = DefaultProgramArgs()
args.inputFiles = []string{"./tests/externalHeader/main/main.c"}
dir, err := ioutil.TempDir("", "c2go_multi4")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // clean up
args.outputFile = path.Join(dir, "multi.go")
args.clangFlags = []string{"-I./tests/externalHeader/include/"}
args.packageName = "main"
args.outputAsTest = true
// testing
err = Start(args)
if err != nil {
t.Errorf(err.Error())
}
// Run Go program
var buf bytes.Buffer
cmd := exec.Command("go", "run", args.outputFile)
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
if err != nil {
t.Errorf(err.Error())
}
if buf.String() != "42" {
t.Errorf("Wrong result: %v", buf.String())
}
}
func TestComments(t *testing.T) {
var args = DefaultProgramArgs()
args.inputFiles = []string{"./tests/comment/main.c"}
dir := "./build/comment"
_ = os.Mkdir(dir, os.ModePerm)
args.outputFile = path.Join(dir, "comment.go")
args.packageName = "main"
args.outputAsTest = true
// testing
err := Start(args)
if err != nil {
t.Errorf(err.Error())
}
dat, err := ioutil.ReadFile(args.outputFile)
if err != nil {
t.Errorf(err.Error())
}
reg := util.GetRegex("comment(\\d+)")
comms := reg.FindAll(dat, -1)
amountComments := 30
for i := range comms {
if fmt.Sprintf("comment%d", i+1) != string(comms[i]) {
t.Fatalf("Not expected name of comment.Expected = %s, actual = %s.",
fmt.Sprintf("comment%d", i+1),
string(comms[i]))
}
}
if len(comms) != amountComments {
t.Fatalf("Expect %d comments, but found %d commnets", amountComments, len(comms))
}
}
func TestCodeQuality(t *testing.T) {
files, err := filepath.Glob("tests/code_quality/*.c")
if err != nil {
t.Fatal(err)
}
// Parallel is not acceptable, before solving issue:
// https://github.com/elliotchance/c2go/issues/376
// t.Parallel()
suffix := ".expected.c"
for i, file := range files {
if strings.HasSuffix(file, suffix) {
continue
}
t.Run(file, func(t *testing.T) {
dir, err := ioutil.TempDir("", fmt.Sprintf("c2go_code_quality_%d_", i))
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // clean up
var args = DefaultProgramArgs()
args.inputFiles = []string{file}
args.outputFile = path.Join(dir, "main.go")
args.packageName = "code_quality"
args.outputAsTest = false
// testing
err = Start(args)
if err != nil {
t.Fatalf(err.Error())
}
goActual, err := cleaningGoCode(args.outputFile)
if err != nil {
t.Fatalf(err.Error())
}
goExpect, err := cleaningGoCode(file[:len(file)-2] + suffix)
if err != nil {
t.Fatalf(err.Error())
}
if bytes.Compare(goActual, goExpect) != 0 {
fmt.Println("actual : ", string(goActual))
fmt.Println("expected : ", string(goExpect))
t.Errorf("Code quality error for : %s", file)
}
})
}
}
func cleaningGoCode(fileName string) (dat []byte, err error) {
// read file
dat, err = ioutil.ReadFile(fileName)
if err != nil {
return
}
// remove comments
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "", string(dat), 0)
if err != nil {
return
}
var buf bytes.Buffer
err = format.Node(&buf, fset, f)
if err != nil {
return
}
// remove all spaces and tabs
dat = bytes.Replace(buf.Bytes(), []byte{' '}, []byte{}, -1)
dat = bytes.Replace(dat, []byte{'\n'}, []byte{}, -1)
dat = bytes.Replace(dat, []byte{'\t'}, []byte{}, -1)
dat = bytes.Replace(dat, []byte{'\r'}, []byte{}, -1)
return
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )