Files
KKRainbow 3d0c9c3ca5 chore(go): use lowercase module import path (#2560)
* chore(go): use lowercase module import path
* ci: scope checks for Go module changes
2026-09-10 15:13:55 +08:00

165 lines
4.4 KiB
Go

package engine
import (
"context"
"errors"
"math"
"reflect"
"testing"
"time"
"github.com/easytier/easytier/easytier-go/internal/coreabi"
)
type recordingCore struct {
calls []string
}
func (*recordingCore) Start(context.Context) error { return nil }
func (*recordingCore) Stop(context.Context) error { return nil }
func (core *recordingCore) Drive(context.Context) (coreabi.State, error) {
core.calls = append(core.calls, "drive")
return coreabi.StateRunning, nil
}
func (core *recordingCore) NotifyCompletions(context.Context) error {
core.calls = append(core.calls, "notify")
return nil
}
func (core *recordingCore) NextDeadline(context.Context) (int64, error) {
core.calls = append(core.calls, "deadline")
return math.MaxInt64, nil
}
func (core *recordingCore) SendPacket(context.Context, []byte) error {
core.calls = append(core.calls, "send")
return nil
}
func (*recordingCore) Drop(context.Context) error { return nil }
func TestCompletionNotifiesGuestBeforeDriving(t *testing.T) {
core := &recordingCore{}
instance := &Instance{
host: &Host{},
ctx: context.Background(),
core: core,
running: make(chan struct{}),
stopped: make(chan struct{}),
}
if _, err := instance.drive(true); err != nil {
t.Fatalf("drive completion: %v", err)
}
want := []string{"notify", "drive", "deadline"}
if !reflect.DeepEqual(core.calls, want) {
t.Fatalf("completion call order = %v, want %v", core.calls, want)
}
}
func TestPacketIngressBatchDrivesOnce(t *testing.T) {
core := &recordingCore{}
instance := &Instance{
host: &Host{},
ctx: context.Background(),
core: core,
commands: make(chan command, maximumPacketIngressBatch),
running: make(chan struct{}),
stopped: make(chan struct{}),
}
requests := make([]command, 3)
for index := range requests {
requests[index] = command{
kind: commandSendPacket,
packet: []byte{byte(index)},
response: make(chan error, 1),
}
if index != 0 {
instance.commands <- requests[index]
}
}
if _, err := instance.handlePacketBatch(requests[0], math.MaxInt64); err != nil {
t.Fatalf("handle packet batch: %v", err)
}
for index := range requests {
if err := <-requests[index].response; err != nil {
t.Fatalf("packet %d response: %v", index, err)
}
}
want := []string{"send", "send", "send", "drive", "deadline"}
if !reflect.DeepEqual(core.calls, want) {
t.Fatalf("packet batch call order = %v, want %v", core.calls, want)
}
}
func TestSendPacketBorrowsBufferUntilGuestConsumesIt(t *testing.T) {
instance := &Instance{
commands: make(chan command, 1),
closeRequested: make(chan struct{}),
done: make(chan struct{}),
}
ctx, cancel := context.WithCancel(context.Background())
packet := []byte{1, 2, 3}
result := make(chan error, 1)
go func() {
result <- instance.SendPacket(ctx, packet)
}()
var request command
select {
case request = <-instance.commands:
case <-time.After(time.Second):
t.Fatal("SendPacket did not enqueue")
}
if &request.packet[0] != &packet[0] {
t.Fatal("SendPacket copied the packet before enqueue")
}
cancel()
select {
case err := <-result:
t.Fatalf("SendPacket returned before guest consumption: %v", err)
case <-time.After(20 * time.Millisecond):
}
request.response <- nil
select {
case err := <-result:
if err != nil {
t.Fatalf("SendPacket after guest consumption: %v", err)
}
case <-time.After(time.Second):
t.Fatal("SendPacket did not return after guest consumption")
}
}
func TestSendPacketBackpressureRemainsCancelableBeforeEnqueue(t *testing.T) {
instance := &Instance{
commands: make(chan command, 1),
closeRequested: make(chan struct{}),
done: make(chan struct{}),
}
instance.commands <- command{kind: commandStart}
ctx, cancel := context.WithCancel(context.Background())
result := make(chan error, 1)
go func() {
result <- instance.SendPacket(ctx, []byte{1})
}()
select {
case err := <-result:
t.Fatalf("SendPacket bypassed full command queue: %v", err)
case <-time.After(20 * time.Millisecond):
}
cancel()
select {
case err := <-result:
if !errors.Is(err, context.Canceled) {
t.Fatalf("SendPacket on full queue returned %v, want context cancellation", err)
}
case <-time.After(time.Second):
t.Fatal("SendPacket on full queue ignored context cancellation")
}
if got := len(instance.commands); got != 1 {
t.Fatalf("queued commands = %d, want the original command only", got)
}
}