fix race in test setup/teardown

not easily triggered, but it happened just now on a build server.
This commit is contained in:
Mechiel Lukkien 2023-08-07 23:14:31 +02:00
parent 849b4ec9e9
commit 49cf16d3f2
No known key found for this signature in database
15 changed files with 29 additions and 36 deletions

View file

@ -38,8 +38,7 @@ func TestCtl(t *testing.T) {
if errs := mox.LoadConfig(ctxbg, true, false); len(errs) > 0 { if errs := mox.LoadConfig(ctxbg, true, false); len(errs) > 0 {
t.Fatalf("loading mox config: %v", errs) t.Fatalf("loading mox config: %v", errs)
} }
switchDone := store.Switchboard() defer store.Switchboard()()
defer close(switchDone)
xlog := mlog.New("ctl") xlog := mlog.New("ctl")

View file

@ -71,8 +71,7 @@ func FuzzServer(f *testing.F) {
if err != nil { if err != nil {
f.Fatalf("set password: %v", err) f.Fatalf("set password: %v", err)
} }
done := store.Switchboard() defer store.Switchboard()()
defer close(done)
comm := store.RegisterComm(acc) comm := store.RegisterComm(acc)
defer comm.Unregister() defer comm.Unregister()

View file

@ -346,11 +346,9 @@ func startArgs(t *testing.T, first, isTLS, allowLoginWithoutTLS bool) *testconn
err = acc.SetPassword("testtest") err = acc.SetPassword("testtest")
tcheck(t, err, "set password") tcheck(t, err, "set password")
} }
var switchDone chan struct{} switchStop := func() {}
if first { if first {
switchDone = store.Switchboard() switchStop = store.Switchboard()
} else {
switchDone = make(chan struct{}) // Dummy, that can be closed.
} }
serverConn, clientConn := net.Pipe() serverConn, clientConn := net.Pipe()
@ -368,7 +366,7 @@ func startArgs(t *testing.T, first, isTLS, allowLoginWithoutTLS bool) *testconn
cid := connCounter cid := connCounter
go func() { go func() {
serve("test", cid, tlsConfig, serverConn, isTLS, allowLoginWithoutTLS) serve("test", cid, tlsConfig, serverConn, isTLS, allowLoginWithoutTLS)
close(switchDone) switchStop()
close(done) close(done)
}() }()
client, err := imapclient.New(clientConn, true) client, err := imapclient.New(clientConn, true)

View file

@ -118,8 +118,7 @@ func xcmdXImport(mbox bool, c *cmd) {
mox.Conf.Dynamic.Accounts = map[string]config.Account{ mox.Conf.Dynamic.Accounts = map[string]config.Account{
account: {}, account: {},
} }
switchDone := store.Switchboard() defer store.Switchboard()()
defer close(switchDone)
xlog := mlog.New("import") xlog := mlog.New("import")
cconn, sconn := net.Pipe() cconn, sconn := net.Pipe()

View file

@ -45,14 +45,14 @@ func setup(t *testing.T) (*store.Account, func()) {
tcheck(t, err, "open account") tcheck(t, err, "open account")
err = acc.SetPassword("testtest") err = acc.SetPassword("testtest")
tcheck(t, err, "set password") tcheck(t, err, "set password")
switchDone := store.Switchboard() switchStop := store.Switchboard()
mox.Shutdown, mox.ShutdownCancel = context.WithCancel(ctxbg) mox.Shutdown, mox.ShutdownCancel = context.WithCancel(ctxbg)
return acc, func() { return acc, func() {
acc.Close() acc.Close()
mox.ShutdownCancel() mox.ShutdownCancel()
mox.Shutdown, mox.ShutdownCancel = context.WithCancel(ctxbg) mox.Shutdown, mox.ShutdownCancel = context.WithCancel(ctxbg)
Shutdown() Shutdown()
close(switchDone) switchStop()
} }
} }

View file

@ -624,7 +624,8 @@ func start(mtastsdbRefresher, skipForkExec bool) error {
http.Serve() http.Serve()
go func() { go func() {
<-store.Switchboard() store.Switchboard()
<-make(chan struct{})
}() }()
return nil return nil
} }

View file

@ -43,8 +43,7 @@ func FuzzServer(f *testing.F) {
if err != nil { if err != nil {
f.Fatalf("set password: %v", err) f.Fatalf("set password: %v", err)
} }
done := store.Switchboard() defer store.Switchboard()()
defer close(done)
err = queue.Init() err = queue.Init()
if err != nil { if err != nil {
f.Fatalf("queue init: %v", err) f.Fatalf("queue init: %v", err)

View file

@ -75,7 +75,7 @@ test email
type testserver struct { type testserver struct {
t *testing.T t *testing.T
acc *store.Account acc *store.Account
switchDone chan struct{} switchStop func()
comm *store.Comm comm *store.Comm
cid int64 cid int64
resolver dns.Resolver resolver dns.Resolver
@ -101,7 +101,7 @@ func newTestServer(t *testing.T, configPath string, resolver dns.Resolver) *test
tcheck(t, err, "open account") tcheck(t, err, "open account")
err = ts.acc.SetPassword("testtest") err = ts.acc.SetPassword("testtest")
tcheck(t, err, "set password") tcheck(t, err, "set password")
ts.switchDone = store.Switchboard() ts.switchStop = store.Switchboard()
err = queue.Init() err = queue.Init()
tcheck(t, err, "queue init") tcheck(t, err, "queue init")
@ -116,7 +116,7 @@ func (ts *testserver) close() {
} }
ts.comm.Unregister() ts.comm.Unregister()
queue.Shutdown() queue.Shutdown()
close(ts.switchDone) ts.switchStop()
err := ts.acc.Close() err := ts.acc.Close()
tcheck(ts.t, err, "closing account") tcheck(ts.t, err, "closing account")
ts.acc = nil ts.acc = nil

View file

@ -36,8 +36,7 @@ func TestMailbox(t *testing.T) {
err = acc.Close() err = acc.Close()
tcheck(t, err, "closing account") tcheck(t, err, "closing account")
}() }()
switchDone := Switchboard() defer Switchboard()()
defer close(switchDone)
log := mlog.New("store") log := mlog.New("store")

View file

@ -25,8 +25,7 @@ func TestExport(t *testing.T) {
acc, err := OpenAccount("mjl") acc, err := OpenAccount("mjl")
tcheck(t, err, "open account") tcheck(t, err, "open account")
defer acc.Close() defer acc.Close()
switchDone := Switchboard() defer Switchboard()()
defer close(switchDone)
log := mlog.New("export") log := mlog.New("export")

View file

@ -101,7 +101,7 @@ type ChangeMailboxKeywords struct {
var switchboardBusy atomic.Bool var switchboardBusy atomic.Bool
// Switchboard distributes changes to accounts to interested listeners. See Comm and Change. // Switchboard distributes changes to accounts to interested listeners. See Comm and Change.
func Switchboard() chan struct{} { func Switchboard() (stop func()) {
regs := map[*Account]map[*Comm]struct{}{} regs := map[*Account]map[*Comm]struct{}{}
done := make(chan struct{}) done := make(chan struct{})
@ -145,14 +145,18 @@ func Switchboard() chan struct{} {
chReq.done <- struct{}{} chReq.done <- struct{}{}
case <-done: case <-done:
if !switchboardBusy.CompareAndSwap(true, false) { done <- struct{}{}
panic("switchboard already unregistered?")
}
return return
} }
} }
}() }()
return done return func() {
done <- struct{}{}
<-done
if !switchboardBusy.CompareAndSwap(true, false) {
panic("switchboard already unregistered?")
}
}
} }
// Comm handles communication with the goroutine that maintains the // Comm handles communication with the goroutine that maintains the

View file

@ -46,8 +46,7 @@ func TestAccount(t *testing.T) {
err = acc.Close() err = acc.Close()
tcheck(t, err, "closing account") tcheck(t, err, "closing account")
}() }()
switchDone := store.Switchboard() defer store.Switchboard()()
defer close(switchDone)
log := mlog.New("store") log := mlog.New("store")

View file

@ -45,8 +45,7 @@ func TestAPI(t *testing.T) {
mox.Context = ctxbg mox.Context = ctxbg
mox.ConfigStaticPath = "../testdata/webmail/mox.conf" mox.ConfigStaticPath = "../testdata/webmail/mox.conf"
mox.MustLoadConfig(true, false) mox.MustLoadConfig(true, false)
switchDone := store.Switchboard() defer store.Switchboard()()
defer close(switchDone)
acc, err := store.OpenAccount("mjl") acc, err := store.OpenAccount("mjl")
tcheck(t, err, "open account") tcheck(t, err, "open account")

View file

@ -25,8 +25,7 @@ func TestView(t *testing.T) {
mox.Context = ctxbg mox.Context = ctxbg
mox.ConfigStaticPath = "../testdata/webmail/mox.conf" mox.ConfigStaticPath = "../testdata/webmail/mox.conf"
mox.MustLoadConfig(true, false) mox.MustLoadConfig(true, false)
switchDone := store.Switchboard() defer store.Switchboard()()
defer close(switchDone)
acc, err := store.OpenAccount("mjl") acc, err := store.OpenAccount("mjl")
tcheck(t, err, "open account") tcheck(t, err, "open account")

View file

@ -266,8 +266,7 @@ func TestWebmail(t *testing.T) {
mox.Context = ctxbg mox.Context = ctxbg
mox.ConfigStaticPath = "../testdata/webmail/mox.conf" mox.ConfigStaticPath = "../testdata/webmail/mox.conf"
mox.MustLoadConfig(true, false) mox.MustLoadConfig(true, false)
switchDone := store.Switchboard() defer store.Switchboard()()
defer close(switchDone)
acc, err := store.OpenAccount("mjl") acc, err := store.OpenAccount("mjl")
tcheck(t, err, "open account") tcheck(t, err, "open account")