Close memory holes, adjust tests
This commit is contained in:
+256
@@ -3635,3 +3635,259 @@ func TestCleanupSessionIfEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoHoldOnDiscardLoopBufferOverflow(t *testing.T) {
|
||||
origSystems := ConfiguredSystem
|
||||
defer func() {
|
||||
ConfiguredSystem = origSystems
|
||||
}()
|
||||
ConfiguredSystem = []TargetSystem{
|
||||
{Name: "DISCARD_SYS", Description: "Discard loop system"},
|
||||
}
|
||||
|
||||
clientServer, clientUser := net.Pipe()
|
||||
defer clientServer.Close()
|
||||
defer clientUser.Close()
|
||||
|
||||
upServer, upClient := net.Pipe()
|
||||
defer upServer.Close()
|
||||
defer upClient.Close()
|
||||
|
||||
initialScreen := []byte{CMD_ERASE_WRITE, 0xC3, 0x11, 0x40, 0x40, 'B', 'A', 'S', 'E', IAC, EOR}
|
||||
|
||||
slot := &SessionSlot{
|
||||
System: ConfiguredSystem[0],
|
||||
Upstream: upClient,
|
||||
LastScreen: initialScreen,
|
||||
MaxBufferSize: 200, // Small buffer so it easily overflows
|
||||
AutoHoldBurstLimit: -1, // Disable burst rate check to test discard check specifically
|
||||
AutoHoldDiscardLimit: 3, // 3 discards triggers auto-hold
|
||||
}
|
||||
|
||||
s := &UserMasterSession{
|
||||
Username: "DISCARDUSER",
|
||||
DisplayTag: "disc1234",
|
||||
TerminalType: "IBM-3278-2",
|
||||
ClientConn: clientServer,
|
||||
ActiveSlotIdx: -1, // Background
|
||||
Slots: []*SessionSlot{slot},
|
||||
IsDisconnected: false,
|
||||
}
|
||||
|
||||
go upstreamReader(s, slot, 0)
|
||||
|
||||
// Send 10 writes of 60 bytes each (each > 200/2, quickly overflowing 200-byte MaxBufferSize)
|
||||
writePayload := []byte{CMD_WRITE, 0xC3, 0x11, 0x40, 0x60}
|
||||
for i := 0; i < 50; i++ {
|
||||
writePayload = append(writePayload, 'X')
|
||||
}
|
||||
writePayload = append(writePayload, IAC, EOR)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err := upServer.Write(writePayload)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write payload %d: %v", i, err)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
slot.Mu.Lock()
|
||||
isHeld := slot.IsHeld
|
||||
screenChanged := slot.ScreenChanged
|
||||
slot.Mu.Unlock()
|
||||
|
||||
if !isHeld {
|
||||
t.Fatal("Expected slot.IsHeld == true after buffer overflow discard loop")
|
||||
}
|
||||
if screenChanged {
|
||||
t.Fatal("Expected slot.ScreenChanged == false once placed on automatic HOLD")
|
||||
}
|
||||
|
||||
// Verify status in main menu is rendered as "HELD" with color Red
|
||||
screen := buildMainMenuScreen(s)
|
||||
var rowHeld *go3270.Field
|
||||
for i := range screen {
|
||||
f := &screen[i]
|
||||
if f.Row == 7 && strings.Contains(f.Content, "DISCARD_SYS") {
|
||||
rowHeld = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if rowHeld == nil {
|
||||
t.Fatal("Expected row for DISCARD_SYS on row 7")
|
||||
}
|
||||
if !strings.Contains(rowHeld.Content, "HELD") {
|
||||
t.Fatalf("Expected row content to contain 'HELD', got: %s", rowHeld.Content)
|
||||
}
|
||||
if rowHeld.Color != go3270.Red {
|
||||
t.Fatalf("Expected HELD row color to be Red, got: %v", rowHeld.Color)
|
||||
}
|
||||
|
||||
// Releasing the hold resets counters
|
||||
slot.Mu.Lock()
|
||||
slot.resetHoldAndLoopCountersLocked()
|
||||
isHeldAfterReset := slot.IsHeld
|
||||
discardCountAfterReset := slot.DiscardCount
|
||||
slot.Mu.Unlock()
|
||||
|
||||
if isHeldAfterReset {
|
||||
t.Fatal("Expected IsHeld == false after resetHoldAndLoopCountersLocked")
|
||||
}
|
||||
if discardCountAfterReset != 0 {
|
||||
t.Fatalf("Expected DiscardCount == 0, got %d", discardCountAfterReset)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoHoldOnExcessiveUpdateRate(t *testing.T) {
|
||||
origSystems := ConfiguredSystem
|
||||
defer func() {
|
||||
ConfiguredSystem = origSystems
|
||||
}()
|
||||
ConfiguredSystem = []TargetSystem{
|
||||
{Name: "BURST_SYS", Description: "Burst loop system"},
|
||||
}
|
||||
|
||||
clientServer, clientUser := net.Pipe()
|
||||
defer clientServer.Close()
|
||||
defer clientUser.Close()
|
||||
|
||||
upServer, upClient := net.Pipe()
|
||||
defer upServer.Close()
|
||||
defer upClient.Close()
|
||||
|
||||
initialScreen := []byte{CMD_ERASE_WRITE, 0xC3, 0x11, 0x40, 0x40, 'B', 'A', 'S', 'E', IAC, EOR}
|
||||
|
||||
slot := &SessionSlot{
|
||||
System: ConfiguredSystem[0],
|
||||
Upstream: upClient,
|
||||
LastScreen: initialScreen,
|
||||
MaxBufferSize: 32768, // Huge buffer, will NOT overflow
|
||||
AutoHoldBurstLimit: 5, // 5 rapid updates triggers auto-hold
|
||||
AutoHoldDiscardLimit: -1, // Disable discard check
|
||||
}
|
||||
|
||||
s := &UserMasterSession{
|
||||
Username: "BURSTUSER",
|
||||
DisplayTag: "brst1234",
|
||||
TerminalType: "IBM-3278-2",
|
||||
ClientConn: clientServer,
|
||||
ActiveSlotIdx: -1, // Background
|
||||
Slots: []*SessionSlot{slot},
|
||||
IsDisconnected: false,
|
||||
}
|
||||
|
||||
go upstreamReader(s, slot, 0)
|
||||
|
||||
// Send 6 small updates in rapid succession
|
||||
smallUpdate := []byte{CMD_WRITE, 0xC3, 0x11, 0x40, 0x50, '1', IAC, EOR}
|
||||
for i := 0; i < 6; i++ {
|
||||
_, err := upServer.Write(smallUpdate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write small update %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
slot.Mu.Lock()
|
||||
isHeld := slot.IsHeld
|
||||
screenChanged := slot.ScreenChanged
|
||||
slot.Mu.Unlock()
|
||||
|
||||
if !isHeld {
|
||||
t.Fatal("Expected slot.IsHeld == true after excessive update rate loop")
|
||||
}
|
||||
if screenChanged {
|
||||
t.Fatal("Expected slot.ScreenChanged == false once placed on automatic HOLD")
|
||||
}
|
||||
|
||||
// Menu renders HELD
|
||||
screen := buildMainMenuScreen(s)
|
||||
var rowHeld *go3270.Field
|
||||
for i := range screen {
|
||||
f := &screen[i]
|
||||
if f.Row == 7 && strings.Contains(f.Content, "BURST_SYS") {
|
||||
rowHeld = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if rowHeld == nil {
|
||||
t.Fatal("Expected row for BURST_SYS on row 7")
|
||||
}
|
||||
if !strings.Contains(rowHeld.Content, "HELD") {
|
||||
t.Fatalf("Expected row content to contain 'HELD', got: %s", rowHeld.Content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveSlotDoesNotAutoHold(t *testing.T) {
|
||||
origSystems := ConfiguredSystem
|
||||
defer func() {
|
||||
ConfiguredSystem = origSystems
|
||||
}()
|
||||
ConfiguredSystem = []TargetSystem{
|
||||
{Name: "ACTIVE_SYS", Description: "Active interactive system"},
|
||||
}
|
||||
|
||||
clientServer, clientUser := net.Pipe()
|
||||
defer clientServer.Close()
|
||||
defer clientUser.Close()
|
||||
|
||||
upServer, upClient := net.Pipe()
|
||||
defer upServer.Close()
|
||||
defer upClient.Close()
|
||||
|
||||
// Drain clientUser in background
|
||||
go func() {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
_, err := clientUser.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
initialScreen := []byte{CMD_ERASE_WRITE, 0xC3, 0x11, 0x40, 0x40, 'B', 'A', 'S', 'E', IAC, EOR}
|
||||
|
||||
slot := &SessionSlot{
|
||||
System: ConfiguredSystem[0],
|
||||
Upstream: upClient,
|
||||
LastScreen: initialScreen,
|
||||
MaxBufferSize: 200,
|
||||
AutoHoldBurstLimit: 5,
|
||||
}
|
||||
|
||||
s := &UserMasterSession{
|
||||
Username: "ACTIVEUSER",
|
||||
DisplayTag: "actv1234",
|
||||
TerminalType: "IBM-3278-2",
|
||||
ClientConn: clientServer,
|
||||
ActiveSlotIdx: 0, // ACTIVE (user is viewing slot 0)
|
||||
Slots: []*SessionSlot{slot},
|
||||
IsDisconnected: false,
|
||||
}
|
||||
|
||||
go upstreamReader(s, slot, 0)
|
||||
|
||||
// Send 10 writes rapidly
|
||||
smallUpdate := []byte{CMD_WRITE, 0xC3, 0x11, 0x40, 0x50, 'Z', IAC, EOR}
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err := upServer.Write(smallUpdate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write update %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
slot.Mu.Lock()
|
||||
isHeld := slot.IsHeld
|
||||
slot.Mu.Unlock()
|
||||
|
||||
if isHeld {
|
||||
t.Fatal("Expected active slot (user watching) to NOT be placed on automatic HOLD")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user