From 4cfde32531d79527368170274322af4f592e4015 Mon Sep 17 00:00:00 2001 From: "diamondburned (Forefront)" Date: Thu, 23 Apr 2020 20:02:58 -0700 Subject: [PATCH] Voice: Added an integration test --- go.mod | 1 + voice/integration_test.go | 144 ++++++++++++++++++++++++++++++++++++++ voice/testdata/nico.dca | Bin 0 -> 22577 bytes 3 files changed, 145 insertions(+) create mode 100644 voice/integration_test.go create mode 100644 voice/testdata/nico.dca diff --git a/go.mod b/go.mod index bc87594..795de84 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/pkg/errors v0.9.1 github.com/sasha-s/go-csync v0.0.0-20160729053059-3bc6c8bdb3fa + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 ) diff --git a/voice/integration_test.go b/voice/integration_test.go new file mode 100644 index 0000000..dd2c2e5 --- /dev/null +++ b/voice/integration_test.go @@ -0,0 +1,144 @@ +// +build integration + +package voice + +import ( + "encoding/binary" + "io" + "log" + "os" + "testing" + + "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/state" +) + +type testConfig struct { + BotToken string + VoiceChID discord.Snowflake +} + +func mustConfig(t *testing.T) testConfig { + var token = os.Getenv("BOT_TOKEN") + if token == "" { + t.Fatal("Missing $BOT_TOKEN") + } + + var sid = os.Getenv("VOICE_ID") + if sid == "" { + t.Fatal("Missing $VOICE_ID") + } + + id, err := discord.ParseSnowflake(sid) + if err != nil { + t.Fatal("Invalid $VOICE_ID:", err) + } + + return testConfig{ + BotToken: token, + VoiceChID: id, + } +} + +// file is only a few bytes lolmao +func nicoReader(t *testing.T) (read func() []byte) { + f, err := os.Open("testdata/nico.dca") + if err != nil { + t.Fatal("Failed to open nico.dca:", err) + } + + t.Cleanup(func() { + f.Close() + }) + + var lenbuf [4]byte + + return func() []byte { + if _, err := io.ReadFull(f, lenbuf[:]); !catchRead(t, err) { + return nil + } + + // Read the integer + framelen := int(binary.LittleEndian.Uint32(lenbuf[:])) + + // Read exactly frame + frame := make([]byte, framelen) + + if _, err := io.ReadFull(f, frame); !catchRead(t, err) { + return nil + } + + return frame + } +} + +func catchRead(t *testing.T, err error) bool { + t.Helper() + + if err == io.EOF { + return false + } + if err != nil { + t.Fatal("Failed to read:", err) + } + return true +} + +func TestIntegration(t *testing.T) { + config := mustConfig(t) + + WSDebug = func(v ...interface{}) { + log.Println(v...) + } + // heart.Debug = func(v ...interface{}) { + // log.Println(append([]interface{}{"Pacemaker:"}, v...)...) + // } + + s, err := state.New("Bot " + config.BotToken) + if err != nil { + t.Fatal("Failed to create a new session:", err) + } + + v := NewVoice(s) + + if err := s.Open(); err != nil { + t.Fatal("Failed to connect:", err) + } + + // Validate the given voice channel. + c, err := s.Channel(config.VoiceChID) + if err != nil { + t.Fatal("Failed to get channel:", err) + } + if c.Type != discord.GuildVoice { + t.Fatal("Channel isn't a guild voice channel.") + } + + conn, err := v.JoinChannel(c.GuildID, c.ID, false, false) + if err != nil { + t.Fatal("Failed to join channel:", err) + } + + // Grab the file in the local test data. + read := nicoReader(t) + + // Trigger speaking. + if err := conn.Speaking(Microphone); err != nil { + t.Fatal("Failed to start speaking:", err) + } + + // Copy the audio? + for bytes := read(); bytes != nil; bytes = read() { + conn.OpusSend <- bytes + // conn.Write(bytes) + } + + // Finish speaking. + if err := conn.StopSpeaking(); err != nil { + t.Fatal("Failed to stop speaking:", err) + } + + if err := conn.Disconnect(s.Gateway); err != nil { + t.Fatal("Failed to disconnect:", err) + } +} diff --git a/voice/testdata/nico.dca b/voice/testdata/nico.dca new file mode 100644 index 0000000000000000000000000000000000000000..e745c2cbc651ee98a48c592379853826b180b403 GIT binary patch literal 22577 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 UyP;Mz(Yl