mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
Re-enable the golangci-lint job in CI (disabled Oct 2025), update to Go 1.25 and golangci-lint-action v7. Fix errcheck, gosimple S1009, staticcheck SA4031 and SA2001 errors across 54 files. Remaining ~39 lint errors will be addressed in follow-up commits.
714 lines
16 KiB
Go
714 lines
16 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"net"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/common/mhfcourse"
|
|
_config "erupe-ce/config"
|
|
"erupe-ce/network/binpacket"
|
|
"erupe-ce/network/mhfpacket"
|
|
)
|
|
|
|
// TestSendServerChatMessage verifies that server chat messages are correctly formatted and queued
|
|
func TestSendServerChatMessage(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
message string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "simple_message",
|
|
message: "Hello, World!",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "empty_message",
|
|
message: "",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "special_characters",
|
|
message: "Test @#$%^&*()",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "unicode_message",
|
|
message: "テスト メッセージ",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "long_message",
|
|
message: strings.Repeat("A", 1000),
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
|
|
// Send the chat message
|
|
sendServerChatMessage(s, tt.message)
|
|
|
|
// Verify the message was queued
|
|
if len(s.sendPackets) == 0 {
|
|
t.Error("no packets were queued")
|
|
return
|
|
}
|
|
|
|
// Read from the channel with timeout to avoid hanging
|
|
select {
|
|
case pkt := <-s.sendPackets:
|
|
if pkt.data == nil {
|
|
t.Error("packet data is nil")
|
|
}
|
|
// Verify it's an MHFPacket (contains opcode)
|
|
if len(pkt.data) < 2 {
|
|
t.Error("packet too short to contain opcode")
|
|
}
|
|
default:
|
|
t.Error("no packet available in channel")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestHandleMsgSysCastBinary_SimpleData verifies basic data message handling
|
|
func TestHandleMsgSysCastBinary_SimpleData(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 54321
|
|
s.stage = NewStage("test_stage")
|
|
s.stage.clients[s] = s.charID
|
|
s.server.sessions = make(map[net.Conn]*Session)
|
|
|
|
// Create a data message payload
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0xDEADBEEF)
|
|
|
|
pkt := &mhfpacket.MsgSysCastBinary{
|
|
Unk: 0,
|
|
BroadcastType: BroadcastTypeStage,
|
|
MessageType: BinaryMessageTypeData,
|
|
RawDataPayload: bf.Data(),
|
|
}
|
|
|
|
// Should not panic
|
|
handleMsgSysCastBinary(s, pkt)
|
|
}
|
|
|
|
// TestHandleMsgSysCastBinary_DiceCommand verifies the @dice command
|
|
func TestHandleMsgSysCastBinary_DiceCommand(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 99999
|
|
s.stage = NewStage("test_stage")
|
|
s.stage.clients[s] = s.charID
|
|
s.server.sessions = make(map[net.Conn]*Session)
|
|
|
|
// Build a chat message with @dice command
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
msg := &binpacket.MsgBinChat{
|
|
Unk0: 0,
|
|
Type: 5,
|
|
Flags: 0x80,
|
|
Message: "@dice",
|
|
SenderName: "TestPlayer",
|
|
}
|
|
_ = msg.Build(bf)
|
|
|
|
pkt := &mhfpacket.MsgSysCastBinary{
|
|
Unk: 0,
|
|
BroadcastType: BroadcastTypeStage,
|
|
MessageType: BinaryMessageTypeChat,
|
|
RawDataPayload: bf.Data(),
|
|
}
|
|
|
|
// Should execute dice command and return
|
|
handleMsgSysCastBinary(s, pkt)
|
|
|
|
// Verify a response was queued (dice result)
|
|
if len(s.sendPackets) == 0 {
|
|
t.Error("dice command did not queue a response")
|
|
}
|
|
}
|
|
|
|
// TestBroadcastTypes verifies different broadcast types are handled
|
|
func TestBroadcastTypes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
broadcastType uint8
|
|
buildPayload func() []byte
|
|
}{
|
|
{
|
|
name: "broadcast_targeted",
|
|
broadcastType: BroadcastTypeTargeted,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetBE() // Targeted uses BE
|
|
msg := &binpacket.MsgBinTargeted{
|
|
TargetCharIDs: []uint32{1, 2, 3},
|
|
RawDataPayload: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
_ = msg.Build(bf)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "broadcast_stage",
|
|
broadcastType: BroadcastTypeStage,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0x12345678)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "broadcast_server",
|
|
broadcastType: BroadcastTypeServer,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0x12345678)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "broadcast_world",
|
|
broadcastType: BroadcastTypeWorld,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0x12345678)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 22222
|
|
s.stage = NewStage("test_stage")
|
|
s.stage.clients[s] = s.charID
|
|
s.server.sessions = make(map[net.Conn]*Session)
|
|
|
|
pkt := &mhfpacket.MsgSysCastBinary{
|
|
Unk: 0,
|
|
BroadcastType: tt.broadcastType,
|
|
MessageType: BinaryMessageTypeState,
|
|
RawDataPayload: tt.buildPayload(),
|
|
}
|
|
|
|
// Should handle without panic
|
|
handleMsgSysCastBinary(s, pkt)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestBinaryMessageTypes verifies different message types are handled
|
|
func TestBinaryMessageTypes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
messageType uint8
|
|
buildPayload func() []byte
|
|
}{
|
|
{
|
|
name: "msg_type_state",
|
|
messageType: BinaryMessageTypeState,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0xDEADBEEF)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "msg_type_chat",
|
|
messageType: BinaryMessageTypeChat,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
msg := &binpacket.MsgBinChat{
|
|
Unk0: 0,
|
|
Type: 5,
|
|
Flags: 0x80,
|
|
Message: "test",
|
|
SenderName: "Player",
|
|
}
|
|
_ = msg.Build(bf)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "msg_type_quest",
|
|
messageType: BinaryMessageTypeQuest,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0xDEADBEEF)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "msg_type_data",
|
|
messageType: BinaryMessageTypeData,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0xDEADBEEF)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "msg_type_mail_notify",
|
|
messageType: BinaryMessageTypeMailNotify,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0xDEADBEEF)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
{
|
|
name: "msg_type_emote",
|
|
messageType: BinaryMessageTypeEmote,
|
|
buildPayload: func() []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0xDEADBEEF)
|
|
return bf.Data()
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 33333
|
|
s.stage = NewStage("test_stage")
|
|
s.stage.clients[s] = s.charID
|
|
s.server.sessions = make(map[net.Conn]*Session)
|
|
|
|
pkt := &mhfpacket.MsgSysCastBinary{
|
|
Unk: 0,
|
|
BroadcastType: BroadcastTypeStage,
|
|
MessageType: tt.messageType,
|
|
RawDataPayload: tt.buildPayload(),
|
|
}
|
|
|
|
// Should handle without panic
|
|
handleMsgSysCastBinary(s, pkt)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestSlicesContainsUsage verifies the slices.Contains function works correctly
|
|
func TestSlicesContainsUsage(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
items []_config.Course
|
|
target _config.Course
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "item_exists",
|
|
items: []_config.Course{
|
|
{Name: "Course1", Enabled: true},
|
|
{Name: "Course2", Enabled: false},
|
|
},
|
|
target: _config.Course{Name: "Course1", Enabled: true},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "item_not_found",
|
|
items: []_config.Course{
|
|
{Name: "Course1", Enabled: true},
|
|
{Name: "Course2", Enabled: false},
|
|
},
|
|
target: _config.Course{Name: "Course3", Enabled: true},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "empty_slice",
|
|
items: []_config.Course{},
|
|
target: _config.Course{Name: "Course1", Enabled: true},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "enabled_mismatch",
|
|
items: []_config.Course{
|
|
{Name: "Course1", Enabled: true},
|
|
},
|
|
target: _config.Course{Name: "Course1", Enabled: false},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := slices.Contains(tt.items, tt.target)
|
|
if result != tt.expected {
|
|
t.Errorf("slices.Contains() = %v, want %v", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestSlicesIndexFuncUsage verifies the slices.IndexFunc function works correctly
|
|
func TestSlicesIndexFuncUsage(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
courses []mhfcourse.Course
|
|
predicate func(mhfcourse.Course) bool
|
|
expected int
|
|
}{
|
|
{
|
|
name: "empty_slice",
|
|
courses: []mhfcourse.Course{},
|
|
predicate: func(c mhfcourse.Course) bool {
|
|
return true
|
|
},
|
|
expected: -1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := slices.IndexFunc(tt.courses, tt.predicate)
|
|
if result != tt.expected {
|
|
t.Errorf("slices.IndexFunc() = %d, want %d", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestChatMessageParsing verifies chat message extraction from binary payload
|
|
func TestChatMessageParsing(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
messageContent string
|
|
authorName string
|
|
}{
|
|
{
|
|
name: "standard_message",
|
|
messageContent: "Hello World",
|
|
authorName: "Player123",
|
|
},
|
|
{
|
|
name: "special_chars_message",
|
|
messageContent: "Test@#$%^&*()",
|
|
authorName: "SpecialUser",
|
|
},
|
|
{
|
|
name: "empty_message",
|
|
messageContent: "",
|
|
authorName: "Silent",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Build a binary chat message
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
msg := &binpacket.MsgBinChat{
|
|
Unk0: 0,
|
|
Type: 5,
|
|
Flags: 0x80,
|
|
Message: tt.messageContent,
|
|
SenderName: tt.authorName,
|
|
}
|
|
_ = msg.Build(bf)
|
|
|
|
// Parse it back
|
|
parseBf := byteframe.NewByteFrameFromBytes(bf.Data())
|
|
parseBf.SetLE()
|
|
_, _ = parseBf.Seek(8, 0) // Skip initial bytes
|
|
|
|
message := string(parseBf.ReadNullTerminatedBytes())
|
|
author := string(parseBf.ReadNullTerminatedBytes())
|
|
|
|
if message != tt.messageContent {
|
|
t.Errorf("message mismatch: got %q, want %q", message, tt.messageContent)
|
|
}
|
|
if author != tt.authorName {
|
|
t.Errorf("author mismatch: got %q, want %q", author, tt.authorName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestBinaryMessageTypeEnums verifies message type constants
|
|
func TestBinaryMessageTypeEnums(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
typeVal uint8
|
|
typeID uint8
|
|
}{
|
|
{
|
|
name: "state_type",
|
|
typeVal: BinaryMessageTypeState,
|
|
typeID: 0,
|
|
},
|
|
{
|
|
name: "chat_type",
|
|
typeVal: BinaryMessageTypeChat,
|
|
typeID: 1,
|
|
},
|
|
{
|
|
name: "quest_type",
|
|
typeVal: BinaryMessageTypeQuest,
|
|
typeID: 2,
|
|
},
|
|
{
|
|
name: "data_type",
|
|
typeVal: BinaryMessageTypeData,
|
|
typeID: 3,
|
|
},
|
|
{
|
|
name: "mail_notify_type",
|
|
typeVal: BinaryMessageTypeMailNotify,
|
|
typeID: 4,
|
|
},
|
|
{
|
|
name: "emote_type",
|
|
typeVal: BinaryMessageTypeEmote,
|
|
typeID: 6,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.typeVal != tt.typeID {
|
|
t.Errorf("type mismatch: got %d, want %d", tt.typeVal, tt.typeID)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestBroadcastTypeEnums verifies broadcast type constants
|
|
func TestBroadcastTypeEnums(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
typeVal uint8
|
|
typeID uint8
|
|
}{
|
|
{
|
|
name: "targeted_type",
|
|
typeVal: BroadcastTypeTargeted,
|
|
typeID: 0x01,
|
|
},
|
|
{
|
|
name: "stage_type",
|
|
typeVal: BroadcastTypeStage,
|
|
typeID: 0x03,
|
|
},
|
|
{
|
|
name: "server_type",
|
|
typeVal: BroadcastTypeServer,
|
|
typeID: 0x06,
|
|
},
|
|
{
|
|
name: "world_type",
|
|
typeVal: BroadcastTypeWorld,
|
|
typeID: 0x0a,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.typeVal != tt.typeID {
|
|
t.Errorf("type mismatch: got %d, want %d", tt.typeVal, tt.typeID)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestPayloadHandling verifies raw payload handling in different scenarios
|
|
func TestPayloadHandling(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
payloadSize int
|
|
broadcastType uint8
|
|
messageType uint8
|
|
}{
|
|
{
|
|
name: "empty_payload",
|
|
payloadSize: 0,
|
|
broadcastType: BroadcastTypeStage,
|
|
messageType: BinaryMessageTypeData,
|
|
},
|
|
{
|
|
name: "small_payload",
|
|
payloadSize: 4,
|
|
broadcastType: BroadcastTypeStage,
|
|
messageType: BinaryMessageTypeData,
|
|
},
|
|
{
|
|
name: "large_payload",
|
|
payloadSize: 10000,
|
|
broadcastType: BroadcastTypeStage,
|
|
messageType: BinaryMessageTypeData,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 44444
|
|
s.stage = NewStage("test_stage")
|
|
s.stage.clients[s] = s.charID
|
|
s.server.sessions = make(map[net.Conn]*Session)
|
|
|
|
// Create payload of specified size
|
|
payload := make([]byte, tt.payloadSize)
|
|
for i := 0; i < len(payload); i++ {
|
|
payload[i] = byte(i % 256)
|
|
}
|
|
|
|
pkt := &mhfpacket.MsgSysCastBinary{
|
|
Unk: 0,
|
|
BroadcastType: tt.broadcastType,
|
|
MessageType: tt.messageType,
|
|
RawDataPayload: payload,
|
|
}
|
|
|
|
// Should handle without panic
|
|
handleMsgSysCastBinary(s, pkt)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCastedBinaryPacketConstruction verifies correct packet construction
|
|
func TestCastedBinaryPacketConstruction(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 77777
|
|
|
|
message := "Test message"
|
|
|
|
sendServerChatMessage(s, message)
|
|
|
|
// Verify a packet was queued
|
|
if len(s.sendPackets) == 0 {
|
|
t.Fatal("no packets queued")
|
|
}
|
|
|
|
// Extract packet from channel
|
|
pkt := <-s.sendPackets
|
|
|
|
if pkt.data == nil {
|
|
t.Error("packet data is nil")
|
|
}
|
|
|
|
// The packet should be at least a valid MHF packet with opcode
|
|
if len(pkt.data) < 2 {
|
|
t.Error("packet too short")
|
|
}
|
|
}
|
|
|
|
// TestNilPayloadHandling verifies safe handling of nil payloads
|
|
func TestNilPayloadHandling(t *testing.T) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 55555
|
|
s.stage = NewStage("test_stage")
|
|
s.stage.clients[s] = s.charID
|
|
s.server.sessions = make(map[net.Conn]*Session)
|
|
|
|
pkt := &mhfpacket.MsgSysCastBinary{
|
|
Unk: 0,
|
|
BroadcastType: BroadcastTypeStage,
|
|
MessageType: BinaryMessageTypeData,
|
|
RawDataPayload: nil,
|
|
}
|
|
|
|
// Should handle nil payload without panic
|
|
handleMsgSysCastBinary(s, pkt)
|
|
}
|
|
|
|
// BenchmarkSendServerChatMessage benchmarks the chat message sending
|
|
func BenchmarkSendServerChatMessage(b *testing.B) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
|
|
message := "This is a benchmark message"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
sendServerChatMessage(s, message)
|
|
}
|
|
}
|
|
|
|
// BenchmarkHandleMsgSysCastBinary benchmarks the packet handling
|
|
func BenchmarkHandleMsgSysCastBinary(b *testing.B) {
|
|
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
|
s := createTestSession(mock)
|
|
s.charID = 99999
|
|
s.stage = NewStage("test_stage")
|
|
s.stage.clients[s] = s.charID
|
|
s.server.sessions = make(map[net.Conn]*Session)
|
|
|
|
// Prepare packet
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
bf.WriteUint32(0x12345678)
|
|
|
|
pkt := &mhfpacket.MsgSysCastBinary{
|
|
Unk: 0,
|
|
BroadcastType: BroadcastTypeStage,
|
|
MessageType: BinaryMessageTypeData,
|
|
RawDataPayload: bf.Data(),
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
handleMsgSysCastBinary(s, pkt)
|
|
}
|
|
}
|
|
|
|
// BenchmarkSlicesContains benchmarks the slices.Contains function
|
|
func BenchmarkSlicesContains(b *testing.B) {
|
|
courses := []_config.Course{
|
|
{Name: "Course1", Enabled: true},
|
|
{Name: "Course2", Enabled: false},
|
|
{Name: "Course3", Enabled: true},
|
|
{Name: "Course4", Enabled: false},
|
|
{Name: "Course5", Enabled: true},
|
|
}
|
|
|
|
target := _config.Course{Name: "Course3", Enabled: true}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = slices.Contains(courses, target)
|
|
}
|
|
}
|
|
|
|
// BenchmarkSlicesIndexFunc benchmarks the slices.IndexFunc function
|
|
func BenchmarkSlicesIndexFunc(b *testing.B) {
|
|
// Create mock courses (empty as real data not needed for benchmark)
|
|
courses := make([]mhfcourse.Course, 100)
|
|
|
|
predicate := func(c mhfcourse.Course) bool {
|
|
return false // Worst case - always iterate to end
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = slices.IndexFunc(courses, predicate)
|
|
}
|
|
}
|