1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2024-11-27 09:12:53 +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

@ -1,55 +1,67 @@
{
"image": "golang:alpine",
"variables": {
"GO111MODULE": "on",
"CGO_ENABLED": "1", # for the race detector
"COV": "/tmp/cov_results",
"dismock": "github.com/mavolin/dismock/v2/pkg/dismock",
"dismock_v": "259685b84e4b6ab364b0fd858aac2aa2dfa42502",
# used only in integration_test
"tested": "./api,./gateway,./bot,./discord"
"image": "golang:alpine",
"variables": {
"GO111MODULE": "on",
"CGO_ENABLED": "1", # for the race detector
"COV": "/tmp/cov_results",
"dismock": "github.com/mavolin/dismock/v2/pkg/dismock",
"dismock_v": "259685b84e4b6ab364b0fd858aac2aa2dfa42502",
# used only in integration_test
"tested": "./api,./gateway,./bot,./discord"
},
"before_script": [
"apk add git build-base"
],
"stages": [
"build",
"test"
],
"build_test": {
"stage": "build",
"script": [
"go build ./..."
]
},
"unit_test": {
"stage": "test",
"timeout": "2m", # 2 minutes
# Don't run the test if we have a $BOT_TOKEN, because
# integration_test will run instead.
"except": {
"variables": [ "$BOT_TOKEN" ]
},
"before_script": [
"apk add git build-base"
],
"stages": [
"build",
"test"
],
"build_test": {
"stage": "build",
"script": [
"go build ./..."
]
"script": [
"go test -coverprofile $COV -tags unitonly -race ./...",
"go tool cover -func $COV"
]
},
"integration_test": {
"stage": "test",
"timeout": "5m", # 5 minutes
# Run the test only if we have $BOT_TOKEN, else fallback to unit
# tests.
"only": {
"variables": [ "$BOT_TOKEN", "$CHANNEL_ID", "$VOICE_ID" ]
},
"unit_test": {
"stage": "test",
"timeout": "2m", # 2 minutes
# Don't run the test if we have a $BOT_TOKEN, because
# integration_test will run instead.
"except": {
"variables": [ "$BOT_TOKEN" ]
},
"script": [
"go test -coverprofile $COV -tags unitonly -race ./...",
"go tool cover -func $COV"
]
"script": [
"go get ./...",
# Test this package along with dismock.
"go get $dismock@$dismock_v",
"go test -coverpkg $tested -coverprofile $COV -race ./... $dismock",
"go mod tidy",
"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" ]
},
"integration_test": {
"stage": "test",
"timeout": "5m", # 5 minutes
# Run the test only if we have $BOT_TOKEN, else fallback to unit
# tests.
"only": {
"variables": [ "$BOT_TOKEN" ]
},
"script": [
"go get ./...",
# Test this package along with dismock.
"go get $dismock@$dismock_v",
"go test -coverpkg $tested -coverprofile $COV -race ./... $dismock",
"go mod tidy",
"go tool cover -func $COV"
]
}
"script": [
"go get ./...",
"go test -race -tags perseverance -run Perseverance -parallel 9 ./..."
]
}
}

View file

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

View file

@ -203,6 +203,11 @@ func (g *Gateway) Close() error {
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("Waiting for the Pacemaker loop to exit.")

View file

@ -1,5 +1,3 @@
// +build !unitonly
package gateway
import (
@ -29,17 +27,14 @@ func TestInvalidToken(t *testing.T) {
t.Fatal("Failed to make a Gateway:", err)
}
err = g.Open()
if err == nil {
if err = g.Open(); err == nil {
t.Fatal("Unexpected success while opening with a bad token.")
}
// 4004 Authentication Failed.
if strings.Contains(err.Error(), "4004") {
return
if !strings.Contains(err.Error(), "4004") {
t.Fatal("Unexpected error:", err)
}
t.Fatal("Unexpected error:", err)
}
func TestIntegration(t *testing.T) {
@ -58,7 +53,7 @@ func TestIntegration(t *testing.T) {
}
g.AddIntents(IntentGuilds)
g.AfterClose = func(err error) {
log.Println("Closed.")
t.Log("Closed.")
}
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"
"sync"
"testing"
"time"
"github.com/diamondburned/arikawa/v2/discord"
"github.com/pkg/errors"
)
const PerseveranceTime = 50 * time.Minute
type Env struct {
BotToken string
ChannelID discord.ChannelID
@ -26,7 +29,7 @@ var (
func Must(t *testing.T) Env {
e, err := GetEnv()
if err != nil {
t.Fatal(err)
t.Skip("integration test variables missing")
}
return e
}

View file

@ -81,6 +81,12 @@ func (p *PacemakerLoop) StartBeating(pace time.Duration, evl EventLoopHandler, e
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
// guarantee that the channel is set when the function returns. This function is
// concurrently safe.
@ -106,7 +112,12 @@ func (p *PacemakerLoop) startLoop() error {
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()
case ev, ok := <-p.events:

View file

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

View file

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