diff --git a/proctl/proctl_test.go b/proctl/proctl_test.go index a5d5f41c..70688767 100644 --- a/proctl/proctl_test.go +++ b/proctl/proctl_test.go @@ -3,29 +3,29 @@ package proctl import ( "bytes" "encoding/binary" - "os" - "os/exec" "path/filepath" "runtime" "testing" + + protest "github.com/derekparker/delve/proctl/test" ) -func withTestProcess(name string, t *testing.T, fn func(p *DebuggedProcess)) { - runtime.LockOSThread() - base := filepath.Base(name) - if err := exec.Command("go", "build", "-gcflags=-N -l", "-o", base, name+".go").Run(); err != nil { - t.Fatalf("Could not compile %s due to %s", name, err) - } - defer os.Remove("./" + base) +func TestMain(m *testing.M) { + protest.RunTestsWithFixtures(m) +} - p, err := Launch([]string{"./" + base}) +func withTestProcess(name string, t *testing.T, fn func(p *DebuggedProcess, fixture protest.Fixture)) { + runtime.LockOSThread() + + fixture := protest.Fixtures[name] + p, err := Launch([]string{fixture.Path}) if err != nil { t.Fatal("Launch():", err) } defer p.Process.Kill() - fn(p) + fn(p, fixture) } func getRegisters(p *DebuggedProcess, t *testing.T) Registers { @@ -72,7 +72,7 @@ func currentLineNumber(p *DebuggedProcess, t *testing.T) (string, int) { } func TestExit(t *testing.T) { - withTestProcess("../_fixtures/continuetestprog", t, func(p *DebuggedProcess) { + withTestProcess("continuetestprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { err := p.Continue() pe, ok := err.(ProcessExitedError) if !ok { @@ -88,7 +88,7 @@ func TestExit(t *testing.T) { } func TestHalt(t *testing.T) { - withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) { + withTestProcess("testprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { go func() { for { if p.Running() { @@ -117,7 +117,7 @@ func TestHalt(t *testing.T) { } func TestStep(t *testing.T) { - withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) { + withTestProcess("testprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { helloworldfunc := p.goSymTable.LookupFunc("main.helloworld") helloworldaddr := helloworldfunc.Entry @@ -139,7 +139,7 @@ func TestStep(t *testing.T) { } func TestBreakPoint(t *testing.T) { - withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) { + withTestProcess("testprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { helloworldfunc := p.goSymTable.LookupFunc("main.helloworld") helloworldaddr := helloworldfunc.Entry @@ -160,7 +160,7 @@ func TestBreakPoint(t *testing.T) { } func TestBreakPointInSeperateGoRoutine(t *testing.T) { - withTestProcess("../_fixtures/testthreads", t, func(p *DebuggedProcess) { + withTestProcess("testthreads", t, func(p *DebuggedProcess, fixture protest.Fixture) { fn := p.goSymTable.LookupFunc("main.anotherthread") if fn == nil { t.Fatal("No fn exists") @@ -189,7 +189,7 @@ func TestBreakPointInSeperateGoRoutine(t *testing.T) { } func TestBreakPointWithNonExistantFunction(t *testing.T) { - withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) { + withTestProcess("testprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { _, err := p.Break(0) if err == nil { t.Fatal("Should not be able to break at non existant function") @@ -198,7 +198,7 @@ func TestBreakPointWithNonExistantFunction(t *testing.T) { } func TestClearBreakPoint(t *testing.T) { - withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) { + withTestProcess("testprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { fn := p.goSymTable.LookupFunc("main.sleepytime") bp, err := p.Break(fn.Entry) assertNoError(err, t, "Break()") @@ -227,8 +227,7 @@ type nextTest struct { } func testnext(testcases []nextTest, initialLocation string, t *testing.T) { - var executablePath = "../_fixtures/testnextprog" - withTestProcess(executablePath, t, func(p *DebuggedProcess) { + withTestProcess("testnextprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { bp, err := p.BreakByLocation(initialLocation) assertNoError(err, t, "Break()") assertNoError(p.Continue(), t, "Continue()") @@ -298,8 +297,7 @@ func TestNextFunctionReturn(t *testing.T) { } func TestRuntimeBreakpoint(t *testing.T) { - var testfile, _ = filepath.Abs("../_fixtures/testruntimebreakpoint") - withTestProcess(testfile, t, func(p *DebuggedProcess) { + withTestProcess("testruntimebreakpoint", t, func(p *DebuggedProcess, fixture protest.Fixture) { err := p.Continue() if err != nil { t.Fatal(err) @@ -316,16 +314,13 @@ func TestRuntimeBreakpoint(t *testing.T) { } func TestFindReturnAddress(t *testing.T) { - var testfile, _ = filepath.Abs("../_fixtures/testnextprog") - - withTestProcess(testfile, t, func(p *DebuggedProcess) { + withTestProcess("testnextprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { var ( fdes = p.frameEntries gsd = p.goSymTable ) - testsourcefile := testfile + ".go" - start, _, err := gsd.LineToPC(testsourcefile, 24) + start, _, err := gsd.LineToPC(fixture.Source, 24) if err != nil { t.Fatal(err) } @@ -369,9 +364,7 @@ func TestFindReturnAddress(t *testing.T) { } func TestSwitchThread(t *testing.T) { - var testfile, _ = filepath.Abs("../_fixtures/testnextprog") - - withTestProcess(testfile, t, func(p *DebuggedProcess) { + withTestProcess("testnextprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { // With invalid thread id err := p.SwitchThread(-1) if err == nil { @@ -412,9 +405,7 @@ func TestSwitchThread(t *testing.T) { } func TestFunctionCall(t *testing.T) { - var testfile, _ = filepath.Abs("../_fixtures/testprog") - - withTestProcess(testfile, t, func(p *DebuggedProcess) { + withTestProcess("testprog", t, func(p *DebuggedProcess, fixture protest.Fixture) { pc, err := p.FindLocation("main.main") if err != nil { t.Fatal(err) diff --git a/proctl/test/support.go b/proctl/test/support.go new file mode 100644 index 00000000..38d1e43d --- /dev/null +++ b/proctl/test/support.go @@ -0,0 +1,87 @@ +package test + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +// Fixture is a test binary. +type Fixture struct { + // Name is the short name of the fixture. + Name string + // Path is the absolute path to the test binary. + Path string + // Source is the absolute path of the test binary source. + Source string +} + +// Fixtures is a map of Fixture.Name to Fixture. +var Fixtures map[string]Fixture = make(map[string]Fixture) + +// RunTestsWithFixtures will pre-compile test fixtures before running test +// methods. Test binaries are deleted before exiting. +func RunTestsWithFixtures(m *testing.M) { + // Find the fixtures directory; this is necessary because the test file's + // nesting level can vary. Only look up to a maxdepth of 10 (which seems + // like a sane alternative to recursion). + parent := ".." + fixturesDir := filepath.Join(parent, "_fixtures") + for depth := 0; depth < 10; depth++ { + if _, err := os.Stat(fixturesDir); err == nil { + break + } + fixturesDir = filepath.Join("..", fixturesDir) + } + if _, err := os.Stat(fixturesDir); err != nil { + fmt.Println("Couldn't locate fixtures directory") + os.Exit(1) + } + + // Collect all files which look like fixture source files. + sources, err := ioutil.ReadDir(fixturesDir) + if err != nil { + fmt.Printf("Couldn't read fixtures dir: %v\n", err) + os.Exit(1) + } + + // Compile the fixtures. + for _, src := range sources { + if src.IsDir() { + continue + } + // Make a (good enough) random temporary file name + r := make([]byte, 4) + rand.Read(r) + path := filepath.Join(fixturesDir, src.Name()) + name := strings.TrimSuffix(src.Name(), filepath.Ext(src.Name())) + tmpfile := filepath.Join(os.TempDir(), fmt.Sprintf("%s.%s", name, hex.EncodeToString(r))) + + // Build the test binary + if err := exec.Command("go", "build", "-gcflags=-N -l", "-o", tmpfile, path).Run(); err != nil { + fmt.Printf("Error compiling %s: %s\n", path, err) + os.Exit(1) + } + fmt.Printf("Compiled test binary %s: %s\n", name, tmpfile) + + source, _ := filepath.Abs(path) + Fixtures[name] = Fixture{Name: name, Path: tmpfile, Source: source} + } + + status := m.Run() + + // Remove the fixtures. + // TODO(danmace): Not sure why yet, but doing these removes in a defer isn't + // working. + for _, f := range Fixtures { + os.Remove(f.Path) + } + + os.Exit(status) +} diff --git a/proctl/variables_test.go b/proctl/variables_test.go index 865b7641..fda9c075 100644 --- a/proctl/variables_test.go +++ b/proctl/variables_test.go @@ -2,9 +2,10 @@ package proctl import ( "fmt" - "path/filepath" "sort" "testing" + + protest "github.com/derekparker/delve/proctl/test" ) type varTest struct { @@ -29,13 +30,6 @@ func assertVariable(t *testing.T, variable *Variable, expected varTest) { } func TestVariableEvaluation(t *testing.T) { - executablePath := "../_fixtures/testvariables" - - fp, err := filepath.Abs(executablePath + ".go") - if err != nil { - t.Fatal(err) - } - testcases := []varTest{ {"a1", "foofoofoofoofoofoo", "struct string", nil}, {"a10", "ofo", "struct string", nil}, @@ -73,8 +67,8 @@ func TestVariableEvaluation(t *testing.T) { {"NonExistent", "", "", fmt.Errorf("could not find symbol value for NonExistent")}, } - withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.goSymTable.LineToPC(fp, 57) + withTestProcess("testvariables", t, func(p *DebuggedProcess, fixture protest.Fixture) { + pc, _, _ := p.goSymTable.LineToPC(fixture.Source, 57) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -97,15 +91,8 @@ func TestVariableEvaluation(t *testing.T) { } func TestVariableFunctionScoping(t *testing.T) { - executablePath := "../_fixtures/testvariables" - - fp, err := filepath.Abs(executablePath + ".go") - if err != nil { - t.Fatal(err) - } - - withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.goSymTable.LineToPC(fp, 57) + withTestProcess("testvariables", t, func(p *DebuggedProcess, fixture protest.Fixture) { + pc, _, _ := p.goSymTable.LineToPC(fixture.Source, 57) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -121,7 +108,7 @@ func TestVariableFunctionScoping(t *testing.T) { assertNoError(err, t, "Unable to find variable a1") // Move scopes, a1 exists here by a2 does not - pc, _, _ = p.goSymTable.LineToPC(fp, 23) + pc, _, _ = p.goSymTable.LineToPC(fixture.Source, 23) _, err = p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -157,13 +144,6 @@ func (s varArray) Less(i, j int) bool { } func TestLocalVariables(t *testing.T) { - executablePath := "../_fixtures/testvariables" - - fp, err := filepath.Abs(executablePath + ".go") - if err != nil { - t.Fatal(err) - } - testcases := []struct { fn func(*ThreadContext) ([]*Variable, error) output []varTest @@ -203,8 +183,8 @@ func TestLocalVariables(t *testing.T) { {"baz", "bazburzum", "struct string", nil}}}, } - withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.goSymTable.LineToPC(fp, 57) + withTestProcess("testvariables", t, func(p *DebuggedProcess, fixture protest.Fixture) { + pc, _, _ := p.goSymTable.LineToPC(fixture.Source, 57) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error") diff --git a/service/rest/integration_test.go b/service/rest/integration_test.go index e11164c4..990803a8 100644 --- a/service/rest/integration_test.go +++ b/service/rest/integration_test.go @@ -1,58 +1,16 @@ package rest import ( - "crypto/rand" - "encoding/hex" - "fmt" - "io/ioutil" "net" - "os" - "os/exec" - "path/filepath" - "strings" "testing" + protest "github.com/derekparker/delve/proctl/test" "github.com/derekparker/delve/service" "github.com/derekparker/delve/service/api" ) -var fixtures map[string]string = make(map[string]string) - func TestMain(m *testing.M) { - fixturesDir := "../../_fixtures" - sources, err := ioutil.ReadDir(fixturesDir) - if err != nil { - fmt.Printf("Couldn't read fixtures dir: %v\n", err) - os.Exit(1) - } - - for _, src := range sources { - if src.IsDir() { - continue - } - // Make a (good enough) random temporary file name - r := make([]byte, 4) - rand.Read(r) - path := filepath.Join(fixturesDir, src.Name()) - name := strings.TrimSuffix(src.Name(), filepath.Ext(src.Name())) - tmpfile := filepath.Join(os.TempDir(), fmt.Sprintf("%s.%s", name, hex.EncodeToString(r))) - - // Build the test binary - if err := exec.Command("go", "build", "-gcflags=-N -l", "-o", tmpfile, path).Run(); err != nil { - fmt.Printf("Error compiling %s: %s\n", path, err) - os.Exit(1) - } - fmt.Printf("Compiled test binary %s: %s\n", name, tmpfile) - fixtures[name] = tmpfile - } - - status := m.Run() - - for _, f := range fixtures { - os.Remove(f) - } - - os.Exit(status) + protest.RunTestsWithFixtures(m) } func withTestClient(name string, t *testing.T, fn func(c service.Client)) { @@ -60,16 +18,13 @@ func withTestClient(name string, t *testing.T, fn func(c service.Client)) { if err != nil { t.Fatalf("couldn't start listener: %s\n", err) } - server := NewServer(&Config{ Listener: listener, - ProcessArgs: []string{fixtures[name]}, + ProcessArgs: []string{protest.Fixtures[name].Path}, }) go server.Run() - client := NewClient(listener.Addr().String()) defer client.Detach(true) - fn(client) }