/* * SPDX-License-Identifier: AGPL-3.0-only * Copyright (c) 2022-2024, daeuniverse Organization */ package outbound import ( "testing" "time" "github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/component/outbound/dialer" "github.com/daeuniverse/dae/pkg/logger" "github.com/daeuniverse/outbound/pkg/fastrand" ) const ( testTcpCheckUrl = "https://connectivitycheck.gstatic.com/generate_204" testUdpCheckDns = "https://connectivitycheck.gstatic.com/generate_204" ) var TestNetworkType = &dialer.NetworkType{ L4Proto: consts.L4ProtoStr_TCP, IpVersion: consts.IpVersionStr_4, IsDns: false, } var log = logger.NewLogger("trace", false, nil) func newDirectDialer(option *dialer.GlobalOption, fullcone bool) *dialer.Dialer { _d, p := dialer.NewDirectDialer(option, true) d := dialer.NewDialer(_d, option, dialer.InstanceOption{DisableCheck: false}, p) return d } func TestDialerGroup_Select_Fixed(t *testing.T) { option := &dialer.GlobalOption{ Log: log, TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: []string{testTcpCheckUrl}}, CheckDnsOptionRaw: dialer.CheckDnsOptionRaw{Raw: []string{testUdpCheckDns}}, CheckInterval: 15 * time.Second, CheckTolerance: 0, CheckDnsTcp: false, } dialers := []*dialer.Dialer{ newDirectDialer(option, true), newDirectDialer(option, false), } fixedIndex := 1 g := NewDialerGroup(option, "test-group", dialers, []*dialer.Annotation{{}}, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_Fixed, FixedIndex: fixedIndex, }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) for i := 0; i < 10; i++ { d, _, err := g.Select(TestNetworkType, false) if err != nil { t.Fatal(err) } if d != dialers[fixedIndex] { t.Fail() } } fixedIndex = 0 g.selectionPolicy.FixedIndex = fixedIndex for i := 0; i < 10; i++ { d, _, err := g.Select(TestNetworkType, false) if err != nil { t.Fatal(err) } if d != dialers[fixedIndex] { t.Fail() } } } func TestDialerGroup_Select_MinLastLatency(t *testing.T) { option := &dialer.GlobalOption{ Log: log, TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: []string{testTcpCheckUrl}}, CheckDnsOptionRaw: dialer.CheckDnsOptionRaw{Raw: []string{testUdpCheckDns}}, CheckInterval: 15 * time.Second, } dialers := []*dialer.Dialer{ newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), } g := NewDialerGroup(option, "test-group", dialers, []*dialer.Annotation{{}}, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_MinLastLatency, }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) // Test 1000 times. for i := 0; i < 1000; i++ { var minLatency time.Duration jMinLatency := -1 for j, d := range dialers { // Simulate a latency test. var ( latency time.Duration alive bool ) // 20% chance for timeout. if fastrand.Intn(5) == 0 { // Simulate a timeout test. latency = 1000 * time.Millisecond alive = false } else { // Simulate a normal test. latency = time.Duration(fastrand.Int63n(int64(1000 * time.Millisecond))) alive = true } d.MustGetLatencies10(TestNetworkType).AppendLatency(latency) if jMinLatency == -1 || latency < minLatency { jMinLatency = j minLatency = latency } g.MustGetAliveDialerSet(TestNetworkType).NotifyLatencyChange(d, alive) } d, _, err := g.Select(TestNetworkType, false) if err != nil { t.Fatal(err) } if d != dialers[jMinLatency] { // Get index of d. indexD := -1 for j := range dialers { if d == dialers[j] { indexD = j break } } t.Errorf("dialers[%v] expected, but dialers[%v] selected", jMinLatency, indexD) } } } func TestDialerGroup_Select_Random(t *testing.T) { option := &dialer.GlobalOption{ Log: log, TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: []string{testTcpCheckUrl}}, CheckDnsOptionRaw: dialer.CheckDnsOptionRaw{Raw: []string{testUdpCheckDns}}, CheckInterval: 15 * time.Second, } dialers := []*dialer.Dialer{ newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), } g := NewDialerGroup(option, "test-group", dialers, []*dialer.Annotation{{}}, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_Random, }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) count := make([]int, len(dialers)) for i := 0; i < 100; i++ { d, _, err := g.Select(TestNetworkType, false) if err != nil { t.Fatal(err) } for j, dd := range dialers { if d == dd { count[j]++ break } } } for i, c := range count { if c == 0 { t.Fail() } t.Logf("count[%v]: %v", i, c) } } func TestDialerGroup_SetAlive(t *testing.T) { option := &dialer.GlobalOption{ Log: log, TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: []string{testTcpCheckUrl}}, CheckDnsOptionRaw: dialer.CheckDnsOptionRaw{Raw: []string{testUdpCheckDns}}, CheckInterval: 15 * time.Second, } dialers := []*dialer.Dialer{ newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), newDirectDialer(option, false), } g := NewDialerGroup(option, "test-group", dialers, []*dialer.Annotation{{}}, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_Random, }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) zeroTarget := 3 g.MustGetAliveDialerSet(TestNetworkType).NotifyLatencyChange(dialers[zeroTarget], false) count := make([]int, len(dialers)) for i := 0; i < 100; i++ { d, _, err := g.Select(TestNetworkType, false) if err != nil { t.Fatal(err) } for j, dd := range dialers { if d == dd { count[j]++ break } } } for i, c := range count { if c == 0 && i != zeroTarget { t.Fail() } t.Logf("count[%v]: %v", i, c) } if count[zeroTarget] != 0 { t.Fail() } }