From 37385f623fbc925b181bc65980671fa7d61813a8 Mon Sep 17 00:00:00 2001 From: Toby Date: Wed, 26 Jul 2023 16:35:25 -0700 Subject: [PATCH] feat(wip): more test reworks --- .../internal/integration_tests/stress_test.go | 1 - extras/obfs/salamander_test.go | 21 +- extras/outbounds/.mockery.yaml | 12 ++ extras/outbounds/interface.go | 23 ++- extras/outbounds/interface_test.go | 139 +++++-------- extras/outbounds/mock_PluggableOutbound.go | 144 ++++++++++++++ extras/outbounds/mock_UDPConn.go | 187 ++++++++++++++++++ extras/outbounds/ob_direct.go | 2 + 8 files changed, 411 insertions(+), 118 deletions(-) create mode 100644 extras/outbounds/.mockery.yaml create mode 100644 extras/outbounds/mock_PluggableOutbound.go create mode 100644 extras/outbounds/mock_UDPConn.go diff --git a/core/internal/integration_tests/stress_test.go b/core/internal/integration_tests/stress_test.go index fa8f248..2324a9e 100644 --- a/core/internal/integration_tests/stress_test.go +++ b/core/internal/integration_tests/stress_test.go @@ -241,7 +241,6 @@ func TestClientServerUDPStress(t *testing.T) { Parallel: 5, Iterations: 2, }).Run) - t.Run("2 Sequential 5 Parallel 200x3k", (&udpStressor{ ListenFunc: c.UDP, ServerAddr: echoAddr, diff --git a/extras/obfs/salamander_test.go b/extras/obfs/salamander_test.go index b11024e..85eafdc 100644 --- a/extras/obfs/salamander_test.go +++ b/extras/obfs/salamander_test.go @@ -1,9 +1,10 @@ package obfs import ( - "bytes" "crypto/rand" "testing" + + "github.com/stretchr/testify/assert" ) func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) { @@ -36,21 +37,9 @@ func TestSalamanderObfuscator(t *testing.T) { for i := 0; i < 1000; i++ { _, _ = rand.Read(in) n := o.Obfuscate(in, oOut) - if n == 0 { - t.Fatal("Failed to obfuscate") - } - if n != len(in)+smSaltLen { - t.Fatal("Wrong obfuscated length") - } + assert.Equal(t, len(in)+smSaltLen, n) n = o.Deobfuscate(oOut[:n], dOut) - if n == 0 { - t.Fatal("Failed to deobfuscate") - } - if n != len(in) { - t.Fatal("Wrong deobfuscated length") - } - if !bytes.Equal(in, dOut[:n]) { - t.Fatal("Deobfuscated data mismatch") - } + assert.Equal(t, len(in), n) + assert.Equal(t, in, dOut[:n]) } } diff --git a/extras/outbounds/.mockery.yaml b/extras/outbounds/.mockery.yaml new file mode 100644 index 0000000..c9e0b71 --- /dev/null +++ b/extras/outbounds/.mockery.yaml @@ -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 diff --git a/extras/outbounds/interface.go b/extras/outbounds/interface.go index 53143d6..8a69127 100644 --- a/extras/outbounds/interface.go +++ b/extras/outbounds/interface.go @@ -21,8 +21,8 @@ import ( // difference, we need a special PluggableOutboundAdapter to convert between the two // for use in Hysteria core config. type PluggableOutbound interface { - DialTCP(reqAddr *AddrEx) (net.Conn, error) - ListenUDP() (UDPConn, error) + TCP(reqAddr *AddrEx) (net.Conn, error) + UDP(reqAddr *AddrEx) (UDPConn, error) } type UDPConn interface { @@ -60,7 +60,7 @@ type PluggableOutboundAdapter struct { 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) if err != nil { return nil, err @@ -69,14 +69,25 @@ func (a *PluggableOutboundAdapter) DialTCP(reqAddr string) (net.Conn, error) { if err != nil { return nil, err } - return a.PluggableOutbound.DialTCP(&AddrEx{ + return a.PluggableOutbound.TCP(&AddrEx{ Host: host, Port: uint16(portInt), }) } -func (a *PluggableOutboundAdapter) DialUDP() (server.UDPConn, error) { - conn, err := a.PluggableOutbound.ListenUDP() +func (a *PluggableOutboundAdapter) UDP(reqAddr string) (server.UDPConn, error) { + 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 { return nil, err } diff --git a/extras/outbounds/interface_test.go b/extras/outbounds/interface_test.go index 3734b66..85a9310 100644 --- a/extras/outbounds/interface_test.go +++ b/extras/outbounds/interface_test.go @@ -1,103 +1,52 @@ package outbounds import ( - "errors" - "net" - "reflect" "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) { - adapter := &PluggableOutboundAdapter{ - PluggableOutbound: &mockPluggableOutbound{}, - } - // TCP with correct addr - _, err := adapter.DialTCP("correct_host_1:34567") - if err != nil { - t.Fatal("TCP with correct addr failed", err) - } - // TCP with wrong addr - _, err = adapter.DialTCP("wrong_host_1:34567") - if err != errWrongAddr { - t.Fatal("TCP with wrong addr should fail, got", err) - } - // DialUDP - uConn, err := adapter.DialUDP() - if err != nil { - t.Fatal("DialUDP failed", err) - } - // ReadFrom - b := make([]byte, 10) - n, addr, err := uConn.ReadFrom(b) - if err != nil { - t.Fatal("ReadFrom failed", err) - } - if n != 10 || addr != "correct_host_2:54321" { - t.Fatalf("ReadFrom got wrong result, n: %d, addr: %s", n, addr) - } - // WriteTo with correct addr - n, err = uConn.WriteTo(b, "correct_host_3:22334") - if err != nil { - t.Fatal("WriteTo with correct addr failed", err) - } - if n != 10 { - t.Fatalf("WriteTo with correct addr got wrong result, n: %d", n) - } - // WriteTo with wrong addr - n, err = uConn.WriteTo(b, "wrong_host_3:22334") - if err != errWrongAddr { - t.Fatal("WriteTo with wrong addr should fail, got", err) - } - // Close - err = uConn.Close() - if err != nil { - t.Fatal("Close failed", err) - } + ob := newMockPluggableOutbound(t) + adapter := &PluggableOutboundAdapter{ob} + + ob.EXPECT().TCP(&AddrEx{ + Host: "only.fans", + Port: 443, + }).Return(nil, nil).Once() + conn, err := adapter.TCP("only.fans:443") + assert.Nil(t, conn) + assert.Nil(t, err) + + mc := newMockUDPConn(t) + mc.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(bs []byte) (int, *AddrEx, error) { + return copy(bs, "gura"), &AddrEx{ + Host: "gura.com", + Port: 2333, + }, nil + }).Once() + mc.EXPECT().WriteTo(mock.Anything, &AddrEx{ + Host: "hololive.tv", + Port: 8999, + }).RunAndReturn(func(bs []byte, addr *AddrEx) (int, error) { + return len(bs), nil + }).Once() + ob.EXPECT().UDP(&AddrEx{ + Host: "hololive.tv", + Port: 8999, + }).Return(mc, nil).Once() + + uConn, err := adapter.UDP("hololive.tv:8999") + assert.Nil(t, err) + assert.NotNil(t, uConn) + n, err := uConn.WriteTo([]byte("gura"), "hololive.tv:8999") + assert.Nil(t, err) + assert.Equal(t, 4, n) + bs := make([]byte, 1024) + n, addr, err := uConn.ReadFrom(bs) + assert.Nil(t, err) + assert.Equal(t, 4, n) + assert.Equal(t, "gura", string(bs[:n])) + assert.Equal(t, "gura.com:2333", addr) } diff --git a/extras/outbounds/mock_PluggableOutbound.go b/extras/outbounds/mock_PluggableOutbound.go new file mode 100644 index 0000000..c8620f2 --- /dev/null +++ b/extras/outbounds/mock_PluggableOutbound.go @@ -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 +} diff --git a/extras/outbounds/mock_UDPConn.go b/extras/outbounds/mock_UDPConn.go new file mode 100644 index 0000000..e71e1da --- /dev/null +++ b/extras/outbounds/mock_UDPConn.go @@ -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 +} diff --git a/extras/outbounds/ob_direct.go b/extras/outbounds/ob_direct.go index 09b4cf2..60bce09 100644 --- a/extras/outbounds/ob_direct.go +++ b/extras/outbounds/ob_direct.go @@ -29,6 +29,7 @@ type directOutbound struct { DeviceName string // For UDP binding } +/* // NewDirectOutboundSimple creates a new directOutbound with the given mode, // without binding to a specific device. Works on all platforms. 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 // AddrEx.ResolveInfo is nil.