代码拉取完成,页面将自动刷新
package traceroute
import (
"context"
"fmt"
"net"
"strings"
"testing"
"time"
)
// ========== 参数测试 ==========
func TestNewTracerouteParam(t *testing.T) {
tests := []struct {
name string
host string
port int
maxHop int
timeout int
expected TracerouteParam
}{
{
name: "默认值测试",
host: "8.8.8.8",
port: 0,
maxHop: 0,
timeout: 0,
expected: TracerouteParam{
Host: "8.8.8.8",
Port: 0,
Protocol: ProtocolICMP,
MaxHop: DefaultMaxHop,
Timeout: DefaultTimeout,
Retries: DefaultRetries,
Privileged: true,
},
},
{
name: "自定义值测试",
host: "github.com",
port: 443,
maxHop: 20,
timeout: 5,
expected: TracerouteParam{
Host: "github.com",
Port: 443,
Protocol: ProtocolICMP,
MaxHop: 20,
Timeout: 5,
Retries: DefaultRetries,
Privileged: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
param := NewTracerouteParam(tt.host, tt.port, tt.maxHop, tt.timeout)
if param.Host != tt.expected.Host {
t.Errorf("Host = %v, want %v", param.Host, tt.expected.Host)
}
if param.Port != tt.expected.Port {
t.Errorf("Port = %v, want %v", param.Port, tt.expected.Port)
}
if param.MaxHop != tt.expected.MaxHop {
t.Errorf("MaxHop = %v, want %v", param.MaxHop, tt.expected.MaxHop)
}
if param.Timeout != tt.expected.Timeout {
t.Errorf("Timeout = %v, want %v", param.Timeout, tt.expected.Timeout)
}
})
}
}
func TestNewTracerouteParamWithProtocol(t *testing.T) {
tests := []struct {
name string
host string
port int
maxHop int
timeout int
protocol Protocol
}{
{"ICMP 协议", "8.8.8.8", 0, 30, 3, ProtocolICMP},
{"TCP 协议", "github.com", 443, 20, 5, ProtocolTCP},
{"UDP 协议", "8.8.8.8", 53, 25, 3, ProtocolUDP},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
param := NewTracerouteParamWithProtocol(tt.host, tt.port, tt.maxHop, tt.timeout, tt.protocol)
if param.Protocol != tt.protocol {
t.Errorf("Protocol = %v, want %v", param.Protocol, tt.protocol)
}
})
}
}
func TestTracerouteParam_Validate(t *testing.T) {
tests := []struct {
name string
param TracerouteParam
wantErr bool
}{
{"空主机", TracerouteParam{Host: ""}, true},
{"无效主机", TracerouteParam{Host: "999.999.999.999"}, true},
{"有效 IP", TracerouteParam{Host: "8.8.8.8"}, false},
{"有效域名", TracerouteParam{Host: "google.com"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.param.Validate()
if (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestTracerouteParam_ResolveTarget(t *testing.T) {
tests := []struct {
name string
host string
wantErr bool
}{
{"IPv4 地址", "8.8.8.8", false},
{"IPv6 地址", "::1", false},
{"有效域名", "localhost", false},
{"无效域名", "this-domain-does-not-exist-12345.com", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
param := TracerouteParam{Host: tt.host}
ip, err := param.ResolveTarget()
if (err != nil) != tt.wantErr {
t.Errorf("ResolveTarget() error = %v, wantErr %v", err, tt.wantErr)
}
if !tt.wantErr && ip == nil {
t.Error("ResolveTarget() returned nil IP")
}
})
}
}
func TestTracerouteParam_GetPort(t *testing.T) {
tests := []struct {
name string
port int
defaultPort int
expected int
}{
{"使用指定端口", 8080, 80, 8080},
{"使用默认端口", 0, 80, 80},
{"负数端口使用默认", -1, 443, 443},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
param := TracerouteParam{Port: tt.port}
result := param.GetPort(tt.defaultPort)
if result != tt.expected {
t.Errorf("GetPort() = %v, want %v", result, tt.expected)
}
})
}
}
// ========== 类型测试 ==========
func TestTracerouteHop_CalculateStats(t *testing.T) {
tests := []struct {
name string
rtts []time.Duration
expected Stats
}{
{
name: "空 RTT",
rtts: []time.Duration{},
expected: Stats{},
},
{
name: "单个 RTT",
rtts: []time.Duration{10 * time.Millisecond},
expected: Stats{
MinRTT: 10 * time.Millisecond,
MaxRTT: 10 * time.Millisecond,
AvgRTT: 10 * time.Millisecond,
TotalRTT: 10 * time.Millisecond,
Count: 1,
},
},
{
name: "多个 RTT",
rtts: []time.Duration{10 * time.Millisecond, 20 * time.Millisecond, 30 * time.Millisecond},
expected: Stats{
MinRTT: 10 * time.Millisecond,
MaxRTT: 30 * time.Millisecond,
AvgRTT: 20 * time.Millisecond,
TotalRTT: 60 * time.Millisecond,
Count: 3,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hop := &TracerouteHop{RTTs: tt.rtts}
stats := hop.CalculateStats()
if stats.MinRTT != tt.expected.MinRTT {
t.Errorf("MinRTT = %v, want %v", stats.MinRTT, tt.expected.MinRTT)
}
if stats.MaxRTT != tt.expected.MaxRTT {
t.Errorf("MaxRTT = %v, want %v", stats.MaxRTT, tt.expected.MaxRTT)
}
if stats.AvgRTT != tt.expected.AvgRTT {
t.Errorf("AvgRTT = %v, want %v", stats.AvgRTT, tt.expected.AvgRTT)
}
if stats.Count != tt.expected.Count {
t.Errorf("Count = %v, want %v", stats.Count, tt.expected.Count)
}
})
}
}
func TestTracerouteHop_IsEmpty(t *testing.T) {
tests := []struct {
name string
hop *TracerouteHop
expected bool
}{
{"nil hop", nil, true},
{"空地址", &TracerouteHop{}, true},
{"有地址", &TracerouteHop{Addr: "8.8.8.8"}, false},
{"有 IP", &TracerouteHop{IP: net.ParseIP("8.8.8.8")}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.hop.IsEmpty()
if result != tt.expected {
t.Errorf("IsEmpty() = %v, want %v", result, tt.expected)
}
})
}
}
func TestTracerouteHop_HasReachedTarget(t *testing.T) {
targetIP := net.ParseIP("8.8.8.8")
tests := []struct {
name string
hop *TracerouteHop
expected bool
}{
{
name: "到达目标",
hop: &TracerouteHop{
Success: true,
IP: targetIP,
},
expected: true,
},
{
name: "未成功",
hop: &TracerouteHop{
Success: false,
IP: targetIP,
},
expected: false,
},
{
name: "不同 IP",
hop: &TracerouteHop{
Success: true,
IP: net.ParseIP("1.1.1.1"),
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.hop.HasReachedTarget(targetIP)
if result != tt.expected {
t.Errorf("HasReachedTarget() = %v, want %v", result, tt.expected)
}
})
}
}
// ========== 格式化测试 ==========
func TestTracerouteHop_FormatAddr(t *testing.T) {
tests := []struct {
name string
hop *TracerouteHop
expected string
}{
{"有地址", &TracerouteHop{Addr: "8.8.8.8"}, "8.8.8.8"},
{"空地址", &TracerouteHop{Addr: ""}, "*"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.hop.FormatAddr()
if result != tt.expected {
t.Errorf("FormatAddr() = %v, want %v", result, tt.expected)
}
})
}
}
func TestTracerouteHop_FormatRTT(t *testing.T) {
tests := []struct {
name string
hop *TracerouteHop
expected string
}{
{"有效 RTT", &TracerouteHop{RTT: 10 * time.Millisecond}, "10ms"},
{"零 RTT", &TracerouteHop{RTT: 0}, "*"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.hop.FormatRTT()
if tt.hop.RTT == 0 {
if result != "*" {
t.Errorf("FormatRTT() = %v, want *", result)
}
}
})
}
}
func TestTracerouteHop_FormatPacketLoss(t *testing.T) {
tests := []struct {
name string
hop *TracerouteHop
expected string
}{
{"0% 丢包", &TracerouteHop{PacketLoss: 0}, "0%"},
{"50% 丢包", &TracerouteHop{PacketLoss: 50}, "50.0%"},
{"100% 丢包", &TracerouteHop{PacketLoss: 100}, "100%"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.hop.FormatPacketLoss()
if result != tt.expected {
t.Errorf("FormatPacketLoss() = %v, want %v", result, tt.expected)
}
})
}
}
func TestTracerouteResult_Format(t *testing.T) {
result := TracerouteResult{
Target: "8.8.8.8",
TargetIP: net.ParseIP("8.8.8.8"),
TargetPort: 0,
Protocol: ProtocolICMP,
TotalHops: 2,
SuccessHops: 2,
Success: true,
TotalTime: 100 * time.Millisecond,
Hops: []*TracerouteHop{
{Hop: 1, Addr: "192.168.1.1", Success: true, RTT: 5 * time.Millisecond},
{Hop: 2, Addr: "8.8.8.8", Success: true, RTT: 10 * time.Millisecond},
},
}
tests := []struct {
name string
format OutputFormat
}{
{"文本格式", FormatText},
{"表格格式", FormatTable},
{"JSON 格式", FormatJSON},
{"CSV 格式", FormatCSV},
{"Markdown 格式", FormatMarkdown},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output := result.Format(tt.format, DefaultFormatOptions())
if output == "" {
t.Errorf("Format() returned empty string")
}
})
}
}
func TestTracerouteResult_FormatJSON(t *testing.T) {
result := TracerouteResult{
Target: "8.8.8.8",
TargetIP: net.ParseIP("8.8.8.8"),
Protocol: ProtocolICMP,
TotalHops: 1,
SuccessHops: 1,
Success: true,
TotalTime: 100 * time.Millisecond,
Hops: []*TracerouteHop{
{Hop: 1, Addr: "8.8.8.8", Success: true, RTT: 10 * time.Millisecond},
},
}
output := result.Format(FormatJSON, DefaultFormatOptions())
if !strings.Contains(output, `"target": "8.8.8.8"`) {
t.Errorf("JSON 格式不包含目标地址")
}
if !strings.Contains(output, `"success": true`) {
t.Errorf("JSON 格式不包含成功状态")
}
}
// ========== 系统命令测试 ==========
func TestTracerouteBySystem(t *testing.T) {
param := NewTracerouteParam("127.0.0.1", 0, 5, 1)
result := param.TracerouteBySystem()
if result.Error != nil && result.Target != "127.0.0.1" {
t.Logf("TracerouteBySystem error: %v (may be expected in some environments)", result.Error)
}
t.Logf("结果: %s", result.FormatHops())
}
// ========== 快速函数测试 ==========
func TestQuickFunctions(t *testing.T) {
// 这些测试主要验证函数能正常执行,不验证具体结果
t.Run("QuickTraceroute", func(t *testing.T) {
// 跳过实际执行,因为需要特权
t.Skip("需要 root 权限")
})
t.Run("QuickTracerouteTCP", func(t *testing.T) {
// 跳过实际执行
t.Skip("需要网络连接")
})
}
// ========== 上下文测试 ==========
func TestTracerouteWithContext_Cancel(t *testing.T) {
param := NewTracerouteParam("8.8.8.8", 0, 30, 3)
ctx, cancel := context.WithCancel(context.Background())
cancel() // 立即取消
result := param.TracerouteWithContext(ctx)
if result.Error == nil {
t.Error("期望返回上下文取消错误")
}
}
func TestTracerouteWithContext_Timeout(t *testing.T) {
param := NewTracerouteParam("8.8.8.8", 0, 30, 3)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
defer cancel()
time.Sleep(1 * time.Millisecond) // 确保超时
result := param.TracerouteWithContext(ctx)
if result.Error == nil {
t.Error("期望返回超时错误")
}
}
// ========== 批量测试 ==========
func TestBatchTraceroute(t *testing.T) {
hosts := []string{"127.0.0.1", "localhost"}
results := BatchTraceroute(hosts, ProtocolICMP)
if len(results) != len(hosts) {
t.Errorf("BatchTraceroute 返回结果数量不正确")
}
for i, result := range results {
t.Logf("Host %s: Success=%v, Hops=%d", hosts[i], result.Success, result.TotalHops)
}
}
// ========== 集成测试(需要权限) ==========
func TestTracerouteICMP_Integration(t *testing.T) {
if testing.Short() {
t.Skip("跳过集成测试")
}
// 注意:ICMP 需要 root 权限
param := NewTracerouteParamWithProtocol("127.0.0.1", 0, 5, 1, ProtocolICMP)
result := param.Traceroute()
t.Logf("ICMP 结果: %s", result.FormatHops())
}
func TestTracerouteTCP_Integration(t *testing.T) {
if testing.Short() {
t.Skip("跳过集成测试")
}
param := NewTracerouteParamWithProtocol("localhost", 80, 5, 1, ProtocolTCP)
result := param.Traceroute()
t.Logf("TCP 结果: %s", result.FormatHops())
}
func TestTracerouteUDP_Integration(t *testing.T) {
if testing.Short() {
t.Skip("跳过集成测试")
}
param := NewTracerouteParamWithProtocol("localhost", 33434, 5, 1, ProtocolUDP)
result := param.Traceroute()
t.Logf("UDP 结果: %s", result.FormatHops())
}
// ========== 示例测试 ==========
func ExampleNewTracerouteParam() {
param := NewTracerouteParam("8.8.8.8", 0, 30, 3)
fmt.Printf("目标: %s, 协议: %s\n", param.Host, param.Protocol)
// Output: 目标: 8.8.8.8, 协议: icmp
}
func ExampleTracerouteResult_FormatHops() {
result := TracerouteResult{
Target: "8.8.8.8",
TargetIP: net.ParseIP("8.8.8.8"),
Protocol: ProtocolICMP,
TotalHops: 1,
SuccessHops: 1,
Success: true,
TotalTime: 100 * time.Millisecond,
Hops: []*TracerouteHop{
{Hop: 1, Addr: "8.8.8.8", Success: true, RTT: 10 * time.Millisecond},
},
}
fmt.Println(result.FormatHops())
}
// ========== 基准测试 ==========
func BenchmarkTracerouteParam_Validate(b *testing.B) {
param := TracerouteParam{
Host: "8.8.8.8",
MaxHop: 30,
Timeout: 3,
Protocol: ProtocolICMP,
}
for i := 0; i < b.N; i++ {
_ = param.Validate()
}
}
func BenchmarkTracerouteParam_ResolveTarget(b *testing.B) {
param := TracerouteParam{Host: "8.8.8.8"}
for i := 0; i < b.N; i++ {
_, _ = param.ResolveTarget()
}
}
func BenchmarkTracerouteResult_Format(b *testing.B) {
result := TracerouteResult{
Target: "8.8.8.8",
TargetIP: net.ParseIP("8.8.8.8"),
Protocol: ProtocolICMP,
TotalHops: 10,
SuccessHops: 10,
TotalTime: 100 * time.Millisecond,
Hops: make([]*TracerouteHop, 10),
}
for i := 0; i < 10; i++ {
result.Hops[i] = &TracerouteHop{
Hop: i + 1,
Addr: "192.168.1.1",
RTT: time.Duration(i+1) * time.Millisecond,
Success: true,
}
}
opts := DefaultFormatOptions()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = result.Format(FormatText, opts)
}
}
func BenchmarkTracerouteResult_FormatJSON(b *testing.B) {
result := TracerouteResult{
Target: "8.8.8.8",
TargetIP: net.ParseIP("8.8.8.8"),
Protocol: ProtocolICMP,
TotalHops: 10,
SuccessHops: 10,
TotalTime: 100 * time.Millisecond,
Hops: make([]*TracerouteHop, 10),
}
for i := 0; i < 10; i++ {
result.Hops[i] = &TracerouteHop{
Hop: i + 1,
Addr: "192.168.1.1",
RTT: time.Duration(i+1) * time.Millisecond,
Success: true,
}
}
opts := DefaultFormatOptions()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = result.Format(FormatJSON, opts)
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。