lib/events: Hack to make test coverage stable from run to run

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3484
This commit is contained in:
Jakob Borg 2016-08-08 18:09:40 +00:00
parent a4ed50ca85
commit c2d8c07137
2 changed files with 78 additions and 58 deletions

View File

@ -9,6 +9,7 @@ package events
import ( import (
"errors" "errors"
"runtime"
stdsync "sync" stdsync "sync"
"time" "time"
@ -47,6 +48,8 @@ const (
AllEvents = (1 << iota) - 1 AllEvents = (1 << iota) - 1
) )
var runningTests = false
func (t EventType) String() string { func (t EventType) String() string {
switch t { switch t {
case Ping: case Ping:
@ -186,6 +189,13 @@ func (l *Logger) Subscribe(mask EventType) *Subscription {
// We need to create the timeout timer in the stopped, non-fired state so // We need to create the timeout timer in the stopped, non-fired state so
// that Subscription.Poll() can safely reset it and select on the timeout // that Subscription.Poll() can safely reset it and select on the timeout
// channel. This ensures the timer is stopped and the channel drained. // channel. This ensures the timer is stopped and the channel drained.
if runningTests {
// Make the behavior stable when running tests to avoid randomly
// varying test coverage. This ensures, in practice if not in
// theory, that the timer fires and we take the true branch of the
// next if.
runtime.Gosched()
}
if !s.timeout.Stop() { if !s.timeout.Stop() {
<-s.timeout.C <-s.timeout.C
} }
@ -231,6 +241,14 @@ func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
if !ok { if !ok {
return e, ErrClosed return e, ErrClosed
} }
if runningTests {
// Make the behavior stable when running tests to avoid randomly
// varying test coverage. This ensures, in practice if not in
// theory, that the timer fires and we take the true branch of
// the next if.
s.timeout.Reset(0)
runtime.Gosched()
}
if !s.timeout.Stop() { if !s.timeout.Stop() {
// The timeout must be stopped and possibly drained to be ready // The timeout must be stopped and possibly drained to be ready
// for reuse in the next call. // for reuse in the next call.

View File

@ -4,27 +4,29 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file, // License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/. // You can obtain one at http://mozilla.org/MPL/2.0/.
package events_test package events
import ( import (
"fmt" "fmt"
"testing" "testing"
"time" "time"
"github.com/syncthing/syncthing/lib/events"
) )
const timeout = 100 * time.Millisecond const timeout = 100 * time.Millisecond
func init() {
runningTests = true
}
func TestNewLogger(t *testing.T) { func TestNewLogger(t *testing.T) {
l := events.NewLogger() l := NewLogger()
if l == nil { if l == nil {
t.Fatal("Unexpected nil Logger") t.Fatal("Unexpected nil Logger")
} }
} }
func TestSubscriber(t *testing.T) { func TestSubscriber(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(0) s := l.Subscribe(0)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
if s == nil { if s == nil {
@ -33,41 +35,41 @@ func TestSubscriber(t *testing.T) {
} }
func TestTimeout(t *testing.T) { func TestTimeout(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(0) s := l.Subscribe(0)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
_, err := s.Poll(timeout) _, err := s.Poll(timeout)
if err != events.ErrTimeout { if err != ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err) t.Fatal("Unexpected non-Timeout error:", err)
} }
} }
func TestEventBeforeSubscribe(t *testing.T) { func TestEventBeforeSubscribe(t *testing.T) {
l := events.NewLogger() l := NewLogger()
l.Log(events.DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
s := l.Subscribe(0) s := l.Subscribe(0)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
_, err := s.Poll(timeout) _, err := s.Poll(timeout)
if err != events.ErrTimeout { if err != ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err) t.Fatal("Unexpected non-Timeout error:", err)
} }
} }
func TestEventAfterSubscribe(t *testing.T) { func TestEventAfterSubscribe(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
l.Log(events.DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
ev, err := s.Poll(timeout) ev, err := s.Poll(timeout)
if err != nil { if err != nil {
t.Fatal("Unexpected error:", err) t.Fatal("Unexpected error:", err)
} }
if ev.Type != events.DeviceConnected { if ev.Type != DeviceConnected {
t.Error("Incorrect event type", ev.Type) t.Error("Incorrect event type", ev.Type)
} }
switch v := ev.Data.(type) { switch v := ev.Data.(type) {
@ -81,27 +83,27 @@ func TestEventAfterSubscribe(t *testing.T) {
} }
func TestEventAfterSubscribeIgnoreMask(t *testing.T) { func TestEventAfterSubscribeIgnoreMask(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.DeviceDisconnected) s := l.Subscribe(DeviceDisconnected)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
l.Log(events.DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
_, err := s.Poll(timeout) _, err := s.Poll(timeout)
if err != events.ErrTimeout { if err != ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err) t.Fatal("Unexpected non-Timeout error:", err)
} }
} }
func TestBufferOverflow(t *testing.T) { func TestBufferOverflow(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
t0 := time.Now() t0 := time.Now()
for i := 0; i < events.BufferSize*2; i++ { for i := 0; i < BufferSize*2; i++ {
l.Log(events.DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
} }
if time.Since(t0) > timeout { if time.Since(t0) > timeout {
t.Fatalf("Logging took too long") t.Fatalf("Logging took too long")
@ -109,10 +111,10 @@ func TestBufferOverflow(t *testing.T) {
} }
func TestUnsubscribe(t *testing.T) { func TestUnsubscribe(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.AllEvents) s := l.Subscribe(AllEvents)
l.Log(events.DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
_, err := s.Poll(timeout) _, err := s.Poll(timeout)
if err != nil { if err != nil {
@ -120,22 +122,22 @@ func TestUnsubscribe(t *testing.T) {
} }
l.Unsubscribe(s) l.Unsubscribe(s)
l.Log(events.DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
_, err = s.Poll(timeout) _, err = s.Poll(timeout)
if err != events.ErrClosed { if err != ErrClosed {
t.Fatal("Unexpected non-Closed error:", err) t.Fatal("Unexpected non-Closed error:", err)
} }
} }
func TestGlobalIDs(t *testing.T) { func TestGlobalIDs(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
l.Log(events.DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
_ = l.Subscribe(events.AllEvents) _ = l.Subscribe(AllEvents)
l.Log(events.DeviceConnected, "bar") l.Log(DeviceConnected, "bar")
ev, err := s.Poll(timeout) ev, err := s.Poll(timeout)
if err != nil { if err != nil {
@ -159,15 +161,15 @@ func TestGlobalIDs(t *testing.T) {
} }
func TestSubscriptionIDs(t *testing.T) { func TestSubscriptionIDs(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.DeviceConnected) s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
l.Log(events.DeviceDisconnected, "a") l.Log(DeviceDisconnected, "a")
l.Log(events.DeviceConnected, "b") l.Log(DeviceConnected, "b")
l.Log(events.DeviceConnected, "c") l.Log(DeviceConnected, "c")
l.Log(events.DeviceDisconnected, "d") l.Log(DeviceDisconnected, "d")
ev, err := s.Poll(timeout) ev, err := s.Poll(timeout)
if err != nil { if err != nil {
@ -193,21 +195,21 @@ func TestSubscriptionIDs(t *testing.T) {
} }
ev, err = s.Poll(timeout) ev, err = s.Poll(timeout)
if err != events.ErrTimeout { if err != ErrTimeout {
t.Fatal("Unexpected error:", err) t.Fatal("Unexpected error:", err)
} }
} }
func TestBufferedSub(t *testing.T) { func TestBufferedSub(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
bs := events.NewBufferedSubscription(s, 10*events.BufferSize) bs := NewBufferedSubscription(s, 10*BufferSize)
go func() { go func() {
for i := 0; i < 10*events.BufferSize; i++ { for i := 0; i < 10*BufferSize; i++ {
l.Log(events.DeviceConnected, fmt.Sprintf("event-%d", i)) l.Log(DeviceConnected, fmt.Sprintf("event-%d", i))
if i%30 == 0 { if i%30 == 0 {
// Give the buffer routine time to pick up the events // Give the buffer routine time to pick up the events
time.Sleep(20 * time.Millisecond) time.Sleep(20 * time.Millisecond)
@ -216,7 +218,7 @@ func TestBufferedSub(t *testing.T) {
}() }()
recv := 0 recv := 0
for recv < 10*events.BufferSize { for recv < 10*BufferSize {
evs := bs.Since(recv, nil) evs := bs.Since(recv, nil)
for _, ev := range evs { for _, ev := range evs {
if ev.GlobalID != recv+1 { if ev.GlobalID != recv+1 {
@ -228,12 +230,12 @@ func TestBufferedSub(t *testing.T) {
} }
func BenchmarkBufferedSub(b *testing.B) { func BenchmarkBufferedSub(b *testing.B) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
bufferSize := events.BufferSize bufferSize := BufferSize
bs := events.NewBufferedSubscription(s, bufferSize) bs := NewBufferedSubscription(s, bufferSize)
// The coord channel paces the sender according to the receiver, // The coord channel paces the sender according to the receiver,
// ensuring that no events are dropped. The benchmark measures sending + // ensuring that no events are dropped. The benchmark measures sending +
@ -249,7 +251,7 @@ func BenchmarkBufferedSub(b *testing.B) {
go func() { go func() {
defer close(done) defer close(done)
recv := 0 recv := 0
var evs []events.Event var evs []Event
for i := 0; i < b.N; { for i := 0; i < b.N; {
evs = bs.Since(recv, evs[:0]) evs = bs.Since(recv, evs[:0])
for _, ev := range evs { for _, ev := range evs {
@ -270,7 +272,7 @@ func BenchmarkBufferedSub(b *testing.B) {
"and": "something else", "and": "something else",
} }
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
l.Log(events.DeviceConnected, eventData) l.Log(DeviceConnected, eventData)
<-coord <-coord
} }
@ -279,16 +281,16 @@ func BenchmarkBufferedSub(b *testing.B) {
} }
func TestSinceUsesSubscriptionId(t *testing.T) { func TestSinceUsesSubscriptionId(t *testing.T) {
l := events.NewLogger() l := NewLogger()
s := l.Subscribe(events.DeviceConnected) s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s) defer l.Unsubscribe(s)
bs := events.NewBufferedSubscription(s, 10*events.BufferSize) bs := NewBufferedSubscription(s, 10*BufferSize)
l.Log(events.DeviceConnected, "a") // SubscriptionID = 1 l.Log(DeviceConnected, "a") // SubscriptionID = 1
l.Log(events.DeviceDisconnected, "b") l.Log(DeviceDisconnected, "b")
l.Log(events.DeviceDisconnected, "c") l.Log(DeviceDisconnected, "c")
l.Log(events.DeviceConnected, "d") // SubscriptionID = 2 l.Log(DeviceConnected, "d") // SubscriptionID = 2
// We need to loop for the events, as they may not all have been // We need to loop for the events, as they may not all have been
// delivered to the buffered subscription when we get here. // delivered to the buffered subscription when we get here.