// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause //go:build linux && amd64 package debug import "testing" func TestConditionEval(t *testing.T) { regs := &Regs{ RAX: 42, RBX: 0, RCX: 100, RIP: 0x1000, RSP: 0x2000, R8: 8, R15: 15, } tests := []struct { cond Condition want bool }{ {Condition{Reg: "rax", Op: "==", Value: 42}, true}, {Condition{Reg: "rax", Op: "==", Value: 43}, false}, {Condition{Reg: "rax", Op: "!=", Value: 43}, true}, {Condition{Reg: "rax", Op: "!=", Value: 42}, false}, {Condition{Reg: "rax", Op: "<", Value: 50}, true}, {Condition{Reg: "rax", Op: "<", Value: 40}, false}, {Condition{Reg: "rax", Op: ">", Value: 40}, true}, {Condition{Reg: "rax", Op: ">", Value: 50}, false}, {Condition{Reg: "rax", Op: "<=", Value: 42}, true}, {Condition{Reg: "rax", Op: ">=", Value: 42}, true}, {Condition{Reg: "rbx", Op: "==", Value: 0}, true}, {Condition{Reg: "rcx", Op: ">", Value: 50}, true}, {Condition{Reg: "rip", Op: "==", Value: 0x1000}, true}, {Condition{Reg: "rsp", Op: ">", Value: 0x1000}, true}, {Condition{Reg: "r8", Op: "==", Value: 8}, true}, {Condition{Reg: "r15", Op: "==", Value: 15}, true}, {Condition{Reg: "eax", Op: "==", Value: 42}, true}, // 32-bit alias {Condition{Reg: "ax", Op: "==", Value: 42}, true}, // 16-bit alias {Condition{Reg: "unknown", Op: "==", Value: 0}, true}, // unknown reg → don't block {Condition{Reg: "rax", Op: "??", Value: 0}, true}, // unknown op → don't block } for _, tt := range tests { got := tt.cond.Eval(regs) if got != tt.want { t.Errorf("Condition{%q %q %d}.Eval() = %v, want %v", tt.cond.Reg, tt.cond.Op, tt.cond.Value, got, tt.want) } } } func TestLineAt(t *testing.T) { lines := []SourceLine{ {Offset: 0, Line: 5}, {Offset: 5, Line: 6}, {Offset: 10, Line: 7}, {Offset: 15, Line: 8}, } tests := []struct { offset int want int }{ {0, 5}, {1, 5}, {4, 5}, {5, 6}, {7, 6}, {10, 7}, {12, 7}, {15, 8}, {20, 8}, } for _, tt := range tests { got := lineAt(lines, tt.offset) if got != tt.want { t.Errorf("lineAt(lines, %d) = %d, want %d", tt.offset, got, tt.want) } } // Empty table. if lineAt(nil, 5) != 0 { t.Error("lineAt(nil, 5) should return 0") } } func TestOffsetForLine(t *testing.T) { lines := []SourceLine{ {Offset: 0, Line: 5}, {Offset: 5, Line: 6}, {Offset: 10, Line: 7}, } tests := []struct { line int want int }{ {5, 0}, {6, 5}, {7, 10}, {99, -1}, // not found {0, -1}, // not found } for _, tt := range tests { got := offsetForLine(lines, tt.line) if got != tt.want { t.Errorf("offsetForLine(lines, %d) = %d, want %d", tt.line, got, tt.want) } } } func TestDecodeRflags(t *testing.T) { tests := []struct { flags uint64 want string }{ {0x202, "IF"}, // only IF set (bit 9) {0x246, "PF ZF IF"}, // PF(2) + ZF(6) + IF(9) {0x001, "CF"}, // carry flag {0x080, "SF"}, // sign flag {0x800, "OF"}, // overflow flag {0x000, "none"}, // no flags {0x202 | 0x001, "CF IF"}, // CF + IF {0x3F7, "CF PF AF ZF SF TF IF"}, // all arithmetic flags } for _, tt := range tests { got := decodeRflags(tt.flags) if got != tt.want { t.Errorf("decodeRflags(%#x) = %q, want %q", tt.flags, got, tt.want) } } } func TestNearestLabel(t *testing.T) { labels := []Label{ {Name: "start", Offset: 0}, {Name: "loop", Offset: 10}, {Name: "done", Offset: 20}, } tests := []struct { offset int want string }{ {0, "start"}, {5, "start"}, {10, "loop"}, {15, "loop"}, {20, "done"}, {25, "done"}, } for _, tt := range tests { got := nearestLabel(labels, tt.offset) if got != tt.want { t.Errorf("nearestLabel(labels, %d) = %q, want %q", tt.offset, got, tt.want) } } }