package stun import ( "errors" "io" "testing" "time" ) type TestAgent struct { f chan Handler } func (n *TestAgent) Close() error { close(n.f) return nil } func (TestAgent) Collect(time.Time) error { return nil } func (TestAgent) Process(m *Message) error { return nil } func (n *TestAgent) Start(id [TransactionIDSize]byte, deadline time.Time, f Handler) error { n.f <- f return nil } func (n *TestAgent) Stop([TransactionIDSize]byte) error { return nil } type noopConnection struct{} func (noopConnection) Write(b []byte) (int, error) { return len(b), nil } func (noopConnection) Read(b []byte) (int, error) { time.Sleep(time.Millisecond) return 0, io.EOF } func (noopConnection) Close() error { return nil } func BenchmarkClient_Do(b *testing.B) { b.ReportAllocs() agent := &TestAgent{ f: make(chan Handler, 1000), } client := NewClient(ClientOptions{ Agent: agent, Connection: noopConnection{}, }) defer client.Close() go func() { e := Event{ Error: nil, Message: nil, } for f := range agent.f { f.HandleEvent(e) } }() m := new(Message) m.Encode() noopF := func(event Event) { // pass } for i := 0; i < b.N; i++ { if err := client.Do(m, time.Time{}, noopF); err != nil { b.Fatal(err) } } } type testConnection struct { write func([]byte) (int, error) b []byte stopped bool } func (t *testConnection) Write(b []byte) (int, error) { return t.write(b) } func (t *testConnection) Close() error { if t.stopped { return errors.New("already stopped") } t.stopped = true return nil } func (t *testConnection) Read(b []byte) (int, error) { if t.stopped { return 0, io.EOF } return copy(b, t.b), nil } func TestClosedOrPanic(t *testing.T) { closedOrPanic(nil) closedOrPanic(ErrAgentClosed) func() { defer func() { r := recover() if r != io.EOF { t.Error(r) } }() closedOrPanic(io.EOF) }() } func TestClient_Do(t *testing.T) { response := MustBuild(TransactionID, BindingSuccess) response.Encode() conn := &testConnection{ b: response.Raw, write: func(bytes []byte) (int, error) { return len(bytes), nil }, } c := NewClient(ClientOptions{ Connection: conn, }) defer func() { if err := c.Close(); err != nil { t.Error(err) } if err := c.Close(); err == nil { t.Error("second close should fail") } }() m := MustBuild( NewTransactionIDSetter(response.TransactionID), ) d := time.Now().Add(time.Second) if err := c.Do(m, d, func(event Event) { if event.Error != nil { t.Error(event.Error) } }); err != nil { t.Error(err) } m = MustBuild(TransactionID) if err := c.Do(m, d, nil); err != nil { t.Error(err) } } func TestCloseErr_Error(t *testing.T) { for id, c := range []struct { Err CloseErr Out string }{ {CloseErr{}, "failed to close: (connection), (agent)"}, {CloseErr{ AgentErr: io.ErrUnexpectedEOF, }, "failed to close: (connection), unexpected EOF (agent)"}, {CloseErr{ ConnectionErr: io.ErrUnexpectedEOF, }, "failed to close: unexpected EOF (connection), (agent)"}, } { if out := c.Err.Error(); out != c.Out { t.Errorf("[%d]: Error(%#v) %q (got) != %q (expected)", id, c.Err, out, c.Out, ) } } } func TestStopErr_Error(t *testing.T) { for id, c := range []struct { Err StopErr Out string }{ {StopErr{}, "error while stopping due to : "}, {StopErr{ Err: io.ErrUnexpectedEOF, }, "error while stopping due to : unexpected EOF"}, {StopErr{ Cause: io.ErrUnexpectedEOF, }, "error while stopping due to unexpected EOF: "}, } { if out := c.Err.Error(); out != c.Out { t.Errorf("[%d]: Error(%#v) %q (got) != %q (expected)", id, c.Err, out, c.Out, ) } } }