// SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT package stun import ( "errors" "testing" "time" "github.com/stretchr/testify/assert" ) func TestAgent_ProcessInTransaction(t *testing.T) { msg := New() agent := NewAgent(func(e Event) { assert.NoError(t, e.Error, "got error") assert.True(t, e.Message.Equal(msg), "%s (got) != %s (expected)", e.Message, msg) }) assert.NoError(t, msg.NewTransactionID()) assert.NoError(t, agent.Start(msg.TransactionID, time.Time{})) assert.NoError(t, agent.Process(msg)) assert.NoError(t, agent.Close()) } func TestAgent_Process(t *testing.T) { msg := New() agent := NewAgent(func(e Event) { assert.NoError(t, e.Error, "got error") assert.True(t, e.Message.Equal(msg), "%s (got) != %s (expected)", e.Message, msg) }) assert.NoError(t, msg.NewTransactionID()) assert.NoError(t, agent.Process(msg)) assert.NoError(t, agent.Close()) assert.ErrorIs(t, agent.Process(msg), ErrAgentClosed) } func TestAgent_Start(t *testing.T) { agent := NewAgent(nil) id := NewTransactionID() deadline := time.Now().AddDate(0, 0, 1) assert.NoError(t, agent.Start(id, deadline), "failed to start transaction") assert.ErrorIs(t, agent.Start(id, deadline), ErrTransactionExists) assert.NoError(t, agent.Close()) id = NewTransactionID() assert.ErrorIs(t, agent.Start(id, deadline), ErrAgentClosed) assert.ErrorIs(t, agent.SetHandler(nil), ErrAgentClosed) } func TestAgent_Stop(t *testing.T) { called := make(chan Event, 1) agent := NewAgent(func(e Event) { called <- e }) assert.ErrorIs(t, agent.Stop(transactionID{}), ErrTransactionNotExists) id := NewTransactionID() timeout := time.Millisecond * 200 assert.NoError(t, agent.Start(id, time.Now().Add(timeout))) assert.NoError(t, agent.Stop(id)) select { case e := <-called: assert.ErrorIs(t, e.Error, ErrTransactionStopped) case <-time.After(timeout * 2): assert.Fail(t, "timed out") } assert.NoError(t, agent.Close()) assert.ErrorIs(t, agent.Close(), ErrAgentClosed) assert.ErrorIs(t, agent.Stop(transactionID{}), ErrAgentClosed) } func TestAgent_GC(t *testing.T) { //nolint:cyclop agent := NewAgent(nil) shouldTimeOutID := make(map[transactionID]bool) deadline := time.Date(2027, time.November, 21, 23, 0, 0, 0, time.UTC, ) gcDeadline := deadline.Add(-time.Second) deadlineNotGC := gcDeadline.AddDate(0, 0, -1) agent.SetHandler(func(e Event) { //nolint:errcheck,gosec id := e.TransactionID shouldTimeOut, found := shouldTimeOutID[id] assert.True(t, found, "unexpected transaction ID") if shouldTimeOut { assert.ErrorIs(t, e.Error, ErrTransactionTimeOut, "%x should time out", id) } else { assert.False(t, errors.Is(e.Error, ErrTransactionTimeOut), "%x should not time out", id) } }) for i := 0; i < 5; i++ { id := NewTransactionID() shouldTimeOutID[id] = false assert.NoError(t, agent.Start(id, deadline)) } for i := 0; i < 5; i++ { id := NewTransactionID() shouldTimeOutID[id] = true assert.NoError(t, agent.Start(id, deadlineNotGC)) } assert.NoError(t, agent.Collect(gcDeadline)) assert.NoError(t, agent.Close()) assert.ErrorIs(t, agent.Collect(gcDeadline), ErrAgentClosed) } func BenchmarkAgent_GC(b *testing.B) { agent := NewAgent(nil) deadline := time.Now().AddDate(0, 0, 1) for i := 0; i < agentCollectCap; i++ { assert.NoError(b, agent.Start(NewTransactionID(), deadline)) } defer func() { assert.NoError(b, agent.Close()) }() b.ReportAllocs() gcDeadline := deadline.Add(-time.Second) for i := 0; i < b.N; i++ { assert.NoError(b, agent.Collect(gcDeadline)) } } func BenchmarkAgent_Process(b *testing.B) { agent := NewAgent(nil) deadline := time.Now().AddDate(0, 0, 1) for i := 0; i < 1000; i++ { assert.NoError(b, agent.Start(NewTransactionID(), deadline)) } defer func() { assert.NoError(b, agent.Close()) }() b.ReportAllocs() m := MustBuild(TransactionID) for i := 0; i < b.N; i++ { assert.NoError(b, agent.Process(m)) } }