1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2025-12-02 09:47:52 +00:00

CI: Add perseverance test for Gateway

This commit is contained in:
diamondburned 2020-12-31 23:48:29 -08:00
parent 7b67a98405
commit c6d8c741e8
9 changed files with 143 additions and 67 deletions

View file

@ -41,7 +41,7 @@
# Run the test only if we have $BOT_TOKEN, else fallback to unit # Run the test only if we have $BOT_TOKEN, else fallback to unit
# tests. # tests.
"only": { "only": {
"variables": [ "$BOT_TOKEN" ] "variables": [ "$BOT_TOKEN", "$CHANNEL_ID", "$VOICE_ID" ]
}, },
"script": [ "script": [
"go get ./...", "go get ./...",
@ -51,5 +51,17 @@
"go mod tidy", "go mod tidy",
"go tool cover -func $COV" "go tool cover -func $COV"
] ]
},
"perseverance_test": {
"stage": "test",
"timeout": "55m", # 55 minuets; check testenv
"interruptible": true, # Stop if newer jobs are available.
"only": {
"variables": [ "$BOT_TOKEN", "$CHANNEL_ID", "$VOICE_ID" ]
},
"script": [
"go get ./...",
"go test -race -tags perseverance -run Perseverance -parallel 9 ./..."
]
} }
} }

View file

@ -1,5 +1,3 @@
// +build !unitonly
package api package api
import ( import (

View file

@ -203,6 +203,11 @@ func (g *Gateway) Close() error {
return nil return nil
} }
// Explicitly signal the pacemaker loop to stop. We should do this in case
// the Start function exited before it could bind the event channel into the
// loop.
g.PacerLoop.Stop()
wsutil.WSDebug("Websocket closed; error:", err) wsutil.WSDebug("Websocket closed; error:", err)
wsutil.WSDebug("Waiting for the Pacemaker loop to exit.") wsutil.WSDebug("Waiting for the Pacemaker loop to exit.")

View file

@ -1,5 +1,3 @@
// +build !unitonly
package gateway package gateway
import ( import (
@ -29,18 +27,15 @@ func TestInvalidToken(t *testing.T) {
t.Fatal("Failed to make a Gateway:", err) t.Fatal("Failed to make a Gateway:", err)
} }
err = g.Open() if err = g.Open(); err == nil {
if err == nil {
t.Fatal("Unexpected success while opening with a bad token.") t.Fatal("Unexpected success while opening with a bad token.")
} }
// 4004 Authentication Failed. // 4004 Authentication Failed.
if strings.Contains(err.Error(), "4004") { if !strings.Contains(err.Error(), "4004") {
return
}
t.Fatal("Unexpected error:", err) t.Fatal("Unexpected error:", err)
} }
}
func TestIntegration(t *testing.T) { func TestIntegration(t *testing.T) {
config := testenv.Must(t) config := testenv.Must(t)
@ -58,7 +53,7 @@ func TestIntegration(t *testing.T) {
} }
g.AddIntents(IntentGuilds) g.AddIntents(IntentGuilds)
g.AfterClose = func(err error) { g.AfterClose = func(err error) {
log.Println("Closed.") t.Log("Closed.")
} }
gateway = g gateway = g

View file

@ -0,0 +1,56 @@
// +build perseverance
package gateway
import (
"testing"
"time"
"github.com/diamondburned/arikawa/v2/internal/testenv"
)
func TestPerseverance(t *testing.T) {
t.Parallel()
config := testenv.Must(t)
g, err := NewGateway("Bot " + config.BotToken)
if err != nil {
t.Fatal("failed to make the gateway:", err)
}
g.AddIntents(IntentGuilds)
if err := g.Open(); err != nil {
t.Fatal("failed to open the gateway:", err)
}
timeout := make(chan struct{}, 1)
// Automatically close the gateway after set duration.
time.AfterFunc(testenv.PerseveranceTime, func() {
t.Log("Perserverence test finshed. Closing gateway.")
timeout <- struct{}{}
if err := g.Close(); err != nil {
t.Error("failed to close gateway:", err)
}
})
// Spin on events.
for ev := range g.Events {
t.Logf("Received event %T.", ev)
}
// Exit gracefully if we have not.
select {
case <-timeout:
return
default:
}
if err := g.Close(); err != nil {
t.Fatal("failed to clean up gateway after fail:", err)
}
t.Fatal("Test failed before timeout.")
}

View file

@ -6,11 +6,14 @@ import (
"os" "os"
"sync" "sync"
"testing" "testing"
"time"
"github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/arikawa/v2/discord"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
const PerseveranceTime = 50 * time.Minute
type Env struct { type Env struct {
BotToken string BotToken string
ChannelID discord.ChannelID ChannelID discord.ChannelID
@ -26,7 +29,7 @@ var (
func Must(t *testing.T) Env { func Must(t *testing.T) Env {
e, err := GetEnv() e, err := GetEnv()
if err != nil { if err != nil {
t.Fatal(err) t.Skip("integration test variables missing")
} }
return e return e
} }

View file

@ -81,6 +81,12 @@ func (p *PacemakerLoop) StartBeating(pace time.Duration, evl EventLoopHandler, e
go func() { exit(p.startLoop()) }() go func() { exit(p.startLoop()) }()
} }
// Stop signals the pacemaker to stop. It does not wait for the pacer to stop.
// The pacer will call the given callback with a nil error.
func (p *PacemakerLoop) Stop() {
close(p.control)
}
// SetEventChannel sets the event channel inside the event loop. There is no // SetEventChannel sets the event channel inside the event loop. There is no
// guarantee that the channel is set when the function returns. This function is // guarantee that the channel is set when the function returns. This function is
// concurrently safe. // concurrently safe.
@ -106,7 +112,12 @@ func (p *PacemakerLoop) startLoop() error {
return errors.Wrap(err, "pace failed, reconnecting") return errors.Wrap(err, "pace failed, reconnecting")
} }
case fn := <-p.control: case fn, ok := <-p.control:
if !ok { // Intentional stop at p.Close().
WSDebug("Pacemaker intentionally stopped using p.control.")
return nil
}
fn() fn()
case ev, ok := <-p.events: case ev, ok := <-p.events:

View file

@ -1,5 +1,3 @@
// +build !unitonly
package voice_test package voice_test
import ( import (

View file

@ -1,5 +1,3 @@
// +build !unitonly
package voice package voice
import ( import (