Merge "integration tests: kill camlistored when all tests are done"

This commit is contained in:
Brad Fitzpatrick 2013-08-02 19:04:41 +00:00 committed by Gerrit Code Review
commit e8d0200a11
6 changed files with 163 additions and 5 deletions

34
pkg/fs/z_test.go Normal file
View File

@ -0,0 +1,34 @@
// +build linux darwin
/*
Copyright 2013 The Camlistore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs
import (
"testing"
"camlistore.org/pkg/test"
)
// Make sure that the camlistored process started
// by the World gets terminated when all the tests
// are done.
// This works only as long as TestZLastTest is the
// last test to run in the package.
func TestZLastTest(t *testing.T) {
test.GetWorldMaybe(t).Stop()
}

View File

@ -0,0 +1,29 @@
// +build !appengine
/*
Copyright 2013 The Camlistore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package osutil
import (
"log"
)
func DieOnParentDeath() {
// TODO(mpl): maybe the way it's done in findproc_normal.go actually works
// on appengine too? Verify that.
log.Fatal("DieOnParentDeath not implemented on appengine.")
}

View File

@ -0,0 +1,43 @@
// +build !appengine
/*
Copyright 2013 The Camlistore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package osutil
import (
"os"
"time"
)
// DieOnParentDeath starts a goroutine that regularly checks that
// the current process can find its parent, and calls os.Exit(0)
// as soon as it cannot.
func DieOnParentDeath() {
// TODO: on Linux, use PR_SET_PDEATHSIG later. For now, the portable way:
go func() {
pollParent(30 * time.Second)
os.Exit(0)
}()
}
// pollParent checks every t that the ppid of the current
// process has not changed (i.e that the process has not
// been orphaned). It returns as soon as that ppid changes.
func pollParent(t time.Duration) {
for initial := os.Getppid(); initial == os.Getppid(); time.Sleep(t) {
}
}

View File

@ -0,0 +1,32 @@
/*
Copyright 2013 The Camlistore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package integration
import (
"testing"
"camlistore.org/pkg/test"
)
// Make sure that the camlistored process started
// by the World gets terminated when all the tests
// are done.
// This works only as long as TestZLastTest is the
// last test to run in the package.
func TestZLastTest(t *testing.T) {
test.GetWorldMaybe(t).Stop()
}

View File

@ -85,12 +85,12 @@ func (w *World) Start() error {
{
cmd := exec.Command("go", "run", "make.go")
cmd.Dir = w.camRoot
log.Printf("Running make.go to build camlistore binaries for testing...")
log.Print("Running make.go to build camlistore binaries for testing...")
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Error building world: %v, %s", err, string(out))
}
log.Printf("Ran make.go.")
log.Print("Ran make.go.")
}
// Start camlistored.
@ -99,12 +99,14 @@ func (w *World) Start() error {
filepath.Join(w.camRoot, "bin", "camlistored"),
"--configfile="+filepath.Join(w.camRoot, "pkg", "test", "testdata", "server-config.json"),
"--listen=FD:3",
"--pollparent=true",
)
var buf bytes.Buffer
w.server.Stdout = &buf
w.server.Stderr = &buf
w.server.Dir = w.tempDir
w.server.Env = append(os.Environ(),
"CAMLI_DEBUG=1",
"CAMLI_ROOT="+w.tempDir,
"CAMLI_SECRET_RING="+filepath.Join(w.camRoot, filepath.FromSlash("pkg/jsonsign/testdata/test-secring.gpg")),
"CAMLI_BASE_URL=http://127.0.0.1:"+strconv.Itoa(w.port),
@ -146,6 +148,9 @@ func (w *World) Start() error {
}
func (w *World) Stop() {
if w == nil {
return
}
w.server.Process.Kill()
if d := w.tempDir; d != "" {
@ -153,7 +158,6 @@ func (w *World) Stop() {
}
}
//
func (w *World) Cmd(binary string, args ...string) *exec.Cmd {
cmd := exec.Command(filepath.Join(w.camRoot, "bin", binary), args...)
switch binary {
@ -199,6 +203,11 @@ func GetWorld(t *testing.T) *World {
return w
}
// GetWorldMaybe returns the current World. It might be nil.
func GetWorldMaybe(t *testing.T) *World {
return theWorld
}
// RunCmd runs c (which is assumed to be something short-lived, like a
// camput or camget command), capturing its stdout for return, and
// also capturing its stderr, just in the case of errors.

View File

@ -35,6 +35,7 @@ import (
"os/signal"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"time"
@ -49,11 +50,11 @@ import (
// Storage options:
_ "camlistore.org/pkg/blobserver/cond"
_ "camlistore.org/pkg/blobserver/encrypt"
_ "camlistore.org/pkg/blobserver/google"
_ "camlistore.org/pkg/blobserver/localdisk"
_ "camlistore.org/pkg/blobserver/remote"
_ "camlistore.org/pkg/blobserver/replica"
_ "camlistore.org/pkg/blobserver/s3"
_ "camlistore.org/pkg/blobserver/google"
_ "camlistore.org/pkg/blobserver/shard"
// Indexers: (also present themselves as storage targets)
_ "camlistore.org/pkg/index" // base indexer + in-memory dev index
@ -76,9 +77,16 @@ var (
flagVersion = flag.Bool("version", false, "show version")
flagConfigFile = flag.String("configfile", "",
"Config file to use, relative to the Camlistore configuration directory root. If blank, the default is used or auto-generated.")
listenFlag = flag.String("listen", "", "host:port to listen on, or :0 to auto-select. If blank, the value in the config will be used instead.")
listenFlag = flag.String("listen", "", "host:port to listen on, or :0 to auto-select. If blank, the value in the config will be used instead.")
flagPollParent bool
)
func init() {
if debug, _ := strconv.ParseBool(os.Getenv("CAMLI_DEBUG")); debug {
flag.BoolVar(&flagPollParent, "pollparent", false, "Camlistored regularly polls its parent process to detect if it has been orphaned, and terminates in that case. Mainly useful for tests.")
}
}
func exitf(pattern string, args ...interface{}) {
if !strings.HasSuffix(pattern, "\n") {
pattern = pattern + "\n"
@ -424,5 +432,8 @@ func main() {
go ws.Serve()
go handleSignals()
if flagPollParent {
osutil.DieOnParentDeath()
}
select {}
}