feat(wip): more test reworks

This commit is contained in:
Toby 2023-07-26 16:35:25 -07:00
parent 6172f2ac53
commit 37385f623f
8 changed files with 411 additions and 118 deletions

View file

@ -241,7 +241,6 @@ func TestClientServerUDPStress(t *testing.T) {
Parallel: 5, Parallel: 5,
Iterations: 2, Iterations: 2,
}).Run) }).Run)
t.Run("2 Sequential 5 Parallel 200x3k", (&udpStressor{ t.Run("2 Sequential 5 Parallel 200x3k", (&udpStressor{
ListenFunc: c.UDP, ListenFunc: c.UDP,
ServerAddr: echoAddr, ServerAddr: echoAddr,

View file

@ -1,9 +1,10 @@
package obfs package obfs
import ( import (
"bytes"
"crypto/rand" "crypto/rand"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) { func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) {
@ -36,21 +37,9 @@ func TestSalamanderObfuscator(t *testing.T) {
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
_, _ = rand.Read(in) _, _ = rand.Read(in)
n := o.Obfuscate(in, oOut) n := o.Obfuscate(in, oOut)
if n == 0 { assert.Equal(t, len(in)+smSaltLen, n)
t.Fatal("Failed to obfuscate")
}
if n != len(in)+smSaltLen {
t.Fatal("Wrong obfuscated length")
}
n = o.Deobfuscate(oOut[:n], dOut) n = o.Deobfuscate(oOut[:n], dOut)
if n == 0 { assert.Equal(t, len(in), n)
t.Fatal("Failed to deobfuscate") assert.Equal(t, in, dOut[:n])
}
if n != len(in) {
t.Fatal("Wrong deobfuscated length")
}
if !bytes.Equal(in, dOut[:n]) {
t.Fatal("Deobfuscated data mismatch")
}
} }
} }

View file

@ -0,0 +1,12 @@
with-expecter: true
inpackage: true
dir: .
packages:
github.com/apernet/hysteria/extras/outbounds:
interfaces:
PluggableOutbound:
config:
mockname: mockPluggableOutbound
UDPConn:
config:
mockname: mockUDPConn

View file

@ -21,8 +21,8 @@ import (
// difference, we need a special PluggableOutboundAdapter to convert between the two // difference, we need a special PluggableOutboundAdapter to convert between the two
// for use in Hysteria core config. // for use in Hysteria core config.
type PluggableOutbound interface { type PluggableOutbound interface {
DialTCP(reqAddr *AddrEx) (net.Conn, error) TCP(reqAddr *AddrEx) (net.Conn, error)
ListenUDP() (UDPConn, error) UDP(reqAddr *AddrEx) (UDPConn, error)
} }
type UDPConn interface { type UDPConn interface {
@ -60,7 +60,7 @@ type PluggableOutboundAdapter struct {
PluggableOutbound PluggableOutbound
} }
func (a *PluggableOutboundAdapter) DialTCP(reqAddr string) (net.Conn, error) { func (a *PluggableOutboundAdapter) TCP(reqAddr string) (net.Conn, error) {
host, port, err := net.SplitHostPort(reqAddr) host, port, err := net.SplitHostPort(reqAddr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -69,14 +69,25 @@ func (a *PluggableOutboundAdapter) DialTCP(reqAddr string) (net.Conn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return a.PluggableOutbound.DialTCP(&AddrEx{ return a.PluggableOutbound.TCP(&AddrEx{
Host: host, Host: host,
Port: uint16(portInt), Port: uint16(portInt),
}) })
} }
func (a *PluggableOutboundAdapter) DialUDP() (server.UDPConn, error) { func (a *PluggableOutboundAdapter) UDP(reqAddr string) (server.UDPConn, error) {
conn, err := a.PluggableOutbound.ListenUDP() host, port, err := net.SplitHostPort(reqAddr)
if err != nil {
return nil, err
}
portInt, err := strconv.Atoi(port)
if err != nil {
return nil, err
}
conn, err := a.PluggableOutbound.UDP(&AddrEx{
Host: host,
Port: uint16(portInt),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -1,103 +1,52 @@
package outbounds package outbounds
import ( import (
"errors"
"net"
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
) )
var errWrongAddr = errors.New("wrong addr")
type mockPluggableOutbound struct{}
func (m *mockPluggableOutbound) DialTCP(reqAddr *AddrEx) (net.Conn, error) {
if !reflect.DeepEqual(reqAddr, &AddrEx{
Host: "correct_host_1",
Port: 34567,
ResolveInfo: nil,
}) {
return nil, errWrongAddr
}
return nil, nil
}
func (m *mockPluggableOutbound) ListenUDP() (UDPConn, error) {
return &mockUDPConn{}, nil
}
type mockUDPConn struct{}
func (u *mockUDPConn) ReadFrom(b []byte) (int, *AddrEx, error) {
for i := range b {
b[i] = 1
}
return len(b), &AddrEx{
Host: "correct_host_2",
Port: 54321,
ResolveInfo: nil,
}, nil
}
func (u *mockUDPConn) WriteTo(b []byte, addr *AddrEx) (int, error) {
if !reflect.DeepEqual(addr, &AddrEx{
Host: "correct_host_3",
Port: 22334,
ResolveInfo: nil,
}) {
return 0, errWrongAddr
}
return len(b), nil
}
func (u *mockUDPConn) Close() error {
return nil
}
func TestPluggableOutboundAdapter(t *testing.T) { func TestPluggableOutboundAdapter(t *testing.T) {
adapter := &PluggableOutboundAdapter{ ob := newMockPluggableOutbound(t)
PluggableOutbound: &mockPluggableOutbound{}, adapter := &PluggableOutboundAdapter{ob}
}
// TCP with correct addr ob.EXPECT().TCP(&AddrEx{
_, err := adapter.DialTCP("correct_host_1:34567") Host: "only.fans",
if err != nil { Port: 443,
t.Fatal("TCP with correct addr failed", err) }).Return(nil, nil).Once()
} conn, err := adapter.TCP("only.fans:443")
// TCP with wrong addr assert.Nil(t, conn)
_, err = adapter.DialTCP("wrong_host_1:34567") assert.Nil(t, err)
if err != errWrongAddr {
t.Fatal("TCP with wrong addr should fail, got", err) mc := newMockUDPConn(t)
} mc.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(bs []byte) (int, *AddrEx, error) {
// DialUDP return copy(bs, "gura"), &AddrEx{
uConn, err := adapter.DialUDP() Host: "gura.com",
if err != nil { Port: 2333,
t.Fatal("DialUDP failed", err) }, nil
} }).Once()
// ReadFrom mc.EXPECT().WriteTo(mock.Anything, &AddrEx{
b := make([]byte, 10) Host: "hololive.tv",
n, addr, err := uConn.ReadFrom(b) Port: 8999,
if err != nil { }).RunAndReturn(func(bs []byte, addr *AddrEx) (int, error) {
t.Fatal("ReadFrom failed", err) return len(bs), nil
} }).Once()
if n != 10 || addr != "correct_host_2:54321" { ob.EXPECT().UDP(&AddrEx{
t.Fatalf("ReadFrom got wrong result, n: %d, addr: %s", n, addr) Host: "hololive.tv",
} Port: 8999,
// WriteTo with correct addr }).Return(mc, nil).Once()
n, err = uConn.WriteTo(b, "correct_host_3:22334")
if err != nil { uConn, err := adapter.UDP("hololive.tv:8999")
t.Fatal("WriteTo with correct addr failed", err) assert.Nil(t, err)
} assert.NotNil(t, uConn)
if n != 10 { n, err := uConn.WriteTo([]byte("gura"), "hololive.tv:8999")
t.Fatalf("WriteTo with correct addr got wrong result, n: %d", n) assert.Nil(t, err)
} assert.Equal(t, 4, n)
// WriteTo with wrong addr bs := make([]byte, 1024)
n, err = uConn.WriteTo(b, "wrong_host_3:22334") n, addr, err := uConn.ReadFrom(bs)
if err != errWrongAddr { assert.Nil(t, err)
t.Fatal("WriteTo with wrong addr should fail, got", err) assert.Equal(t, 4, n)
} assert.Equal(t, "gura", string(bs[:n]))
// Close assert.Equal(t, "gura.com:2333", addr)
err = uConn.Close()
if err != nil {
t.Fatal("Close failed", err)
}
} }

View file

@ -0,0 +1,144 @@
// Code generated by mockery v2.32.0. DO NOT EDIT.
package outbounds
import (
net "net"
mock "github.com/stretchr/testify/mock"
)
// mockPluggableOutbound is an autogenerated mock type for the PluggableOutbound type
type mockPluggableOutbound struct {
mock.Mock
}
type mockPluggableOutbound_Expecter struct {
mock *mock.Mock
}
func (_m *mockPluggableOutbound) EXPECT() *mockPluggableOutbound_Expecter {
return &mockPluggableOutbound_Expecter{mock: &_m.Mock}
}
// TCP provides a mock function with given fields: reqAddr
func (_m *mockPluggableOutbound) TCP(reqAddr *AddrEx) (net.Conn, error) {
ret := _m.Called(reqAddr)
var r0 net.Conn
var r1 error
if rf, ok := ret.Get(0).(func(*AddrEx) (net.Conn, error)); ok {
return rf(reqAddr)
}
if rf, ok := ret.Get(0).(func(*AddrEx) net.Conn); ok {
r0 = rf(reqAddr)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(net.Conn)
}
}
if rf, ok := ret.Get(1).(func(*AddrEx) error); ok {
r1 = rf(reqAddr)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// mockPluggableOutbound_TCP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TCP'
type mockPluggableOutbound_TCP_Call struct {
*mock.Call
}
// TCP is a helper method to define mock.On call
// - reqAddr *AddrEx
func (_e *mockPluggableOutbound_Expecter) TCP(reqAddr interface{}) *mockPluggableOutbound_TCP_Call {
return &mockPluggableOutbound_TCP_Call{Call: _e.mock.On("TCP", reqAddr)}
}
func (_c *mockPluggableOutbound_TCP_Call) Run(run func(reqAddr *AddrEx)) *mockPluggableOutbound_TCP_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(*AddrEx))
})
return _c
}
func (_c *mockPluggableOutbound_TCP_Call) Return(_a0 net.Conn, _a1 error) *mockPluggableOutbound_TCP_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *mockPluggableOutbound_TCP_Call) RunAndReturn(run func(*AddrEx) (net.Conn, error)) *mockPluggableOutbound_TCP_Call {
_c.Call.Return(run)
return _c
}
// UDP provides a mock function with given fields: reqAddr
func (_m *mockPluggableOutbound) UDP(reqAddr *AddrEx) (UDPConn, error) {
ret := _m.Called(reqAddr)
var r0 UDPConn
var r1 error
if rf, ok := ret.Get(0).(func(*AddrEx) (UDPConn, error)); ok {
return rf(reqAddr)
}
if rf, ok := ret.Get(0).(func(*AddrEx) UDPConn); ok {
r0 = rf(reqAddr)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(UDPConn)
}
}
if rf, ok := ret.Get(1).(func(*AddrEx) error); ok {
r1 = rf(reqAddr)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// mockPluggableOutbound_UDP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UDP'
type mockPluggableOutbound_UDP_Call struct {
*mock.Call
}
// UDP is a helper method to define mock.On call
// - reqAddr *AddrEx
func (_e *mockPluggableOutbound_Expecter) UDP(reqAddr interface{}) *mockPluggableOutbound_UDP_Call {
return &mockPluggableOutbound_UDP_Call{Call: _e.mock.On("UDP", reqAddr)}
}
func (_c *mockPluggableOutbound_UDP_Call) Run(run func(reqAddr *AddrEx)) *mockPluggableOutbound_UDP_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(*AddrEx))
})
return _c
}
func (_c *mockPluggableOutbound_UDP_Call) Return(_a0 UDPConn, _a1 error) *mockPluggableOutbound_UDP_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *mockPluggableOutbound_UDP_Call) RunAndReturn(run func(*AddrEx) (UDPConn, error)) *mockPluggableOutbound_UDP_Call {
_c.Call.Return(run)
return _c
}
// newMockPluggableOutbound creates a new instance of mockPluggableOutbound. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func newMockPluggableOutbound(t interface {
mock.TestingT
Cleanup(func())
}) *mockPluggableOutbound {
mock := &mockPluggableOutbound{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View file

@ -0,0 +1,187 @@
// Code generated by mockery v2.32.0. DO NOT EDIT.
package outbounds
import mock "github.com/stretchr/testify/mock"
// mockUDPConn is an autogenerated mock type for the UDPConn type
type mockUDPConn struct {
mock.Mock
}
type mockUDPConn_Expecter struct {
mock *mock.Mock
}
func (_m *mockUDPConn) EXPECT() *mockUDPConn_Expecter {
return &mockUDPConn_Expecter{mock: &_m.Mock}
}
// Close provides a mock function with given fields:
func (_m *mockUDPConn) Close() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// mockUDPConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
type mockUDPConn_Close_Call struct {
*mock.Call
}
// Close is a helper method to define mock.On call
func (_e *mockUDPConn_Expecter) Close() *mockUDPConn_Close_Call {
return &mockUDPConn_Close_Call{Call: _e.mock.On("Close")}
}
func (_c *mockUDPConn_Close_Call) Run(run func()) *mockUDPConn_Close_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *mockUDPConn_Close_Call) Return(_a0 error) *mockUDPConn_Close_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *mockUDPConn_Close_Call) RunAndReturn(run func() error) *mockUDPConn_Close_Call {
_c.Call.Return(run)
return _c
}
// ReadFrom provides a mock function with given fields: b
func (_m *mockUDPConn) ReadFrom(b []byte) (int, *AddrEx, error) {
ret := _m.Called(b)
var r0 int
var r1 *AddrEx
var r2 error
if rf, ok := ret.Get(0).(func([]byte) (int, *AddrEx, error)); ok {
return rf(b)
}
if rf, ok := ret.Get(0).(func([]byte) int); ok {
r0 = rf(b)
} else {
r0 = ret.Get(0).(int)
}
if rf, ok := ret.Get(1).(func([]byte) *AddrEx); ok {
r1 = rf(b)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*AddrEx)
}
}
if rf, ok := ret.Get(2).(func([]byte) error); ok {
r2 = rf(b)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// mockUDPConn_ReadFrom_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadFrom'
type mockUDPConn_ReadFrom_Call struct {
*mock.Call
}
// ReadFrom is a helper method to define mock.On call
// - b []byte
func (_e *mockUDPConn_Expecter) ReadFrom(b interface{}) *mockUDPConn_ReadFrom_Call {
return &mockUDPConn_ReadFrom_Call{Call: _e.mock.On("ReadFrom", b)}
}
func (_c *mockUDPConn_ReadFrom_Call) Run(run func(b []byte)) *mockUDPConn_ReadFrom_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].([]byte))
})
return _c
}
func (_c *mockUDPConn_ReadFrom_Call) Return(_a0 int, _a1 *AddrEx, _a2 error) *mockUDPConn_ReadFrom_Call {
_c.Call.Return(_a0, _a1, _a2)
return _c
}
func (_c *mockUDPConn_ReadFrom_Call) RunAndReturn(run func([]byte) (int, *AddrEx, error)) *mockUDPConn_ReadFrom_Call {
_c.Call.Return(run)
return _c
}
// WriteTo provides a mock function with given fields: b, addr
func (_m *mockUDPConn) WriteTo(b []byte, addr *AddrEx) (int, error) {
ret := _m.Called(b, addr)
var r0 int
var r1 error
if rf, ok := ret.Get(0).(func([]byte, *AddrEx) (int, error)); ok {
return rf(b, addr)
}
if rf, ok := ret.Get(0).(func([]byte, *AddrEx) int); ok {
r0 = rf(b, addr)
} else {
r0 = ret.Get(0).(int)
}
if rf, ok := ret.Get(1).(func([]byte, *AddrEx) error); ok {
r1 = rf(b, addr)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// mockUDPConn_WriteTo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteTo'
type mockUDPConn_WriteTo_Call struct {
*mock.Call
}
// WriteTo is a helper method to define mock.On call
// - b []byte
// - addr *AddrEx
func (_e *mockUDPConn_Expecter) WriteTo(b interface{}, addr interface{}) *mockUDPConn_WriteTo_Call {
return &mockUDPConn_WriteTo_Call{Call: _e.mock.On("WriteTo", b, addr)}
}
func (_c *mockUDPConn_WriteTo_Call) Run(run func(b []byte, addr *AddrEx)) *mockUDPConn_WriteTo_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].([]byte), args[1].(*AddrEx))
})
return _c
}
func (_c *mockUDPConn_WriteTo_Call) Return(_a0 int, _a1 error) *mockUDPConn_WriteTo_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *mockUDPConn_WriteTo_Call) RunAndReturn(run func([]byte, *AddrEx) (int, error)) *mockUDPConn_WriteTo_Call {
_c.Call.Return(run)
return _c
}
// newMockUDPConn creates a new instance of mockUDPConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func newMockUDPConn(t interface {
mock.TestingT
Cleanup(func())
}) *mockUDPConn {
mock := &mockUDPConn{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View file

@ -29,6 +29,7 @@ type directOutbound struct {
DeviceName string // For UDP binding DeviceName string // For UDP binding
} }
/*
// NewDirectOutboundSimple creates a new directOutbound with the given mode, // NewDirectOutboundSimple creates a new directOutbound with the given mode,
// without binding to a specific device. Works on all platforms. // without binding to a specific device. Works on all platforms.
func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound { func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound {
@ -39,6 +40,7 @@ func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound {
}, },
} }
} }
*/
// resolve is our built-in DNS resolver for handling the case when // resolve is our built-in DNS resolver for handling the case when
// AddrEx.ResolveInfo is nil. // AddrEx.ResolveInfo is nil.