79 lines
2.2 KiB
Go
79 lines
2.2 KiB
Go
package main
|
|
|
|
// Gate: a push whose file stops answering (a network mount that died while
|
|
// the job reads it) does not stay pending: a read of the file that does not
|
|
// return within the bound fails the job with a sentence, whether it is stuck
|
|
// checking the digest or sending.
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// deadMountReads makes every read of a pushed file block until the test ends,
|
|
// then fail, as a read of a mount that went away does.
|
|
func deadMountReads(t *testing.T) {
|
|
t.Helper()
|
|
gate := make(chan struct{})
|
|
var once sync.Once
|
|
backupPushHookMu.Lock()
|
|
prev := backupPushRead
|
|
backupPushRead = func(f *os.File, p []byte) (int, error) {
|
|
<-gate
|
|
return 0, errors.New("the mount went away")
|
|
}
|
|
backupPushHookMu.Unlock()
|
|
t.Cleanup(func() {
|
|
once.Do(func() { close(gate) })
|
|
backupPushHookMu.Lock()
|
|
backupPushRead = prev
|
|
backupPushHookMu.Unlock()
|
|
})
|
|
}
|
|
|
|
func TestAPushFromAFolderThatStopsAnsweringFails(t *testing.T) {
|
|
allowLoopbackFetch(t)
|
|
backupPushHookMu.Lock()
|
|
prevStall := backupPushFileStall
|
|
backupPushFileStall = 200 * time.Millisecond
|
|
backupPushHookMu.Unlock()
|
|
t.Cleanup(func() { backupPushHookMu.Lock(); backupPushFileStall = prevStall; backupPushHookMu.Unlock() })
|
|
|
|
for _, phase := range []string{"checking the digest", "sending"} {
|
|
t.Run(phase, func(t *testing.T) {
|
|
root := t.TempDir()
|
|
_, sum := pushFixture(t, root, []byte(strings.Repeat("a backup ", 1000)))
|
|
abs := filepath.Join(root, fixtureData)
|
|
if phase == "sending" {
|
|
// The digest is known, so the first read is the upload's.
|
|
fi, err := os.Lstat(abs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
noteHash(abs, fi, sum)
|
|
}
|
|
srv, _, _ := uploadServer(t, 200)
|
|
deadMountReads(t)
|
|
job := pushJob(root, sum)
|
|
done := make(chan struct{})
|
|
go func() { runBackupPush(job, srv.URL+"/put", abs); close(done) }()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("a push whose file stopped answering is still running")
|
|
}
|
|
backupJobsMu.Lock()
|
|
state, msg := job.State, job.Error
|
|
backupJobsMu.Unlock()
|
|
if state != "failed" || !strings.Contains(msg, "stopped answering") {
|
|
t.Fatalf("the job reads %s %q", state, msg)
|
|
}
|
|
})
|
|
}
|
|
}
|