From 0e0adc6a641af5b1d11f19601a4b72571c7cdb48 Mon Sep 17 00:00:00 2001 From: Lexer747 Date: Sun, 31 Aug 2025 21:16:35 +0100 Subject: [PATCH] Save Memory with fieldalignment (#30) This patch uses the `fieldalignment` tool to save memory across the board. I don't think this will dramatically improve performance but it's "free" memory savings. Most of the hot paths haven't changed because the savings are all related to moving pointers. Including tests this saves 856 bytes. Excluding tests it's still 552 bytes saved. This patch also adds the `fieldalignment` lint to CI so that I actually maintain these savings. Closes #26 --- .github/workflows/ci.yml | 25 +++++++++-- .golangci.yaml | 1 + cmd/subcommands/acci-ping/gui.go | 2 +- cmd/tab_completion/tab_completion.go | 2 +- cmd/tab_completion/tabflags/tabflags.go | 13 +++--- graph/data/data.go | 4 +- graph/data/data_test.go | 11 +++-- graph/data/serialisation_test.go | 2 +- graph/draw_window.go | 7 ++- graph/drawing.go | 2 +- graph/graph.go | 58 ++++++++++--------------- graph/graph_file_test.go | 4 +- graph/graph_test.go | 4 +- graph/graphdata/graphdata.go | 4 +- graph/graphdata/graphdata_test.go | 2 +- graph/spinner.go | 2 +- graph/xAxis.go | 6 +-- graph/yAxis.go | 2 +- gui/box_test.go | 4 +- gui/notifications.go | 2 +- gui/themes/colours_test.go | 2 +- gui/themes/themes_test.go | 4 +- ping/addr.go | 3 +- ping/api.go | 21 +++++---- ping/api_implementation.go | 5 +-- terminal/parser.go | 7 ++- terminal/terminal.go | 43 +++++++----------- tools/fieldalignment-lint.sh | 16 +++++++ tools/verify.sh | 3 +- utils/numeric/numeric_test.go | 10 +++-- utils/sliceutils/sliceutils_test.go | 2 +- utils/th/helper_debug.go | 2 +- utils/th/test_helper.go | 21 +++++---- 33 files changed, 151 insertions(+), 145 deletions(-) create mode 100755 tools/fieldalignment-lint.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb8717b..20d083a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5.3.0 with: - go-version: 'stable' + go-version: '1.25.0' - name: Build run: ./tools/build.sh @@ -29,7 +29,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5.3.0 with: - go-version: 'stable' + go-version: '1.25.0' - name: Test run: go test -race ./... @@ -44,7 +44,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5.3.0 with: - go-version: 'stable' + go-version: '1.25.0' - name: golangci-lint uses: golangci/golangci-lint-action@v7.0.0 with: @@ -52,4 +52,21 @@ jobs: verify: false # This linter works based on the file modification when running under github actions because it's # not given full access to the git history and therefore gets the wrong date for all files. - args: --disable goheader \ No newline at end of file + args: --disable goheader + + fieldalignment: + name: fieldalignment + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5.3.0 + with: + go-version: '1.25.0' + + - name: install fieldalignment + run: go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest + + - name: check for diff + run: $GITHUB_WORKSPACE/tools/fieldalignment-lint.sh diff --git a/.golangci.yaml b/.golangci.yaml index f26e9e9..ebf9ca4 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -7,6 +7,7 @@ linters: disable: - cyclop - durationcheck + - embeddedstructfieldcheck # we use [fieldalignment] instead - errname - exhaustruct - forbidigo diff --git a/cmd/subcommands/acci-ping/gui.go b/cmd/subcommands/acci-ping/gui.go index 34f5295..aed679b 100644 --- a/cmd/subcommands/acci-ping/gui.go +++ b/cmd/subcommands/acci-ping/gui.go @@ -13,8 +13,8 @@ import ( type GUI struct { listeningChars map[rune]terminal.ConditionalListener - fallbacks []terminal.Listener GUIState *gui.GUIState + fallbacks []terminal.Listener } func newGUIState() *GUI { diff --git a/cmd/tab_completion/tab_completion.go b/cmd/tab_completion/tab_completion.go index 36d425c..79c86b4 100644 --- a/cmd/tab_completion/tab_completion.go +++ b/cmd/tab_completion/tab_completion.go @@ -29,8 +29,8 @@ const debug = false const AutoCompleteString = "b2827fc8fc8c8267cb15f5a925de7e4712aa04ef2fbd43458326b595d66a36d9" // sha256 of `autoCompleteString` type Command struct { - Cmd string Fs *tabflags.FlagSet + Cmd string } // Run will for a given command line input, write to stdout the autocomplete suggestion for the input [base] diff --git a/cmd/tab_completion/tabflags/tabflags.go b/cmd/tab_completion/tabflags/tabflags.go index db7acf7..ccceeed 100644 --- a/cmd/tab_completion/tabflags/tabflags.go +++ b/cmd/tab_completion/tabflags/tabflags.go @@ -16,27 +16,26 @@ import ( ) type AutoComplete struct { + FileExt string Choices []string WantsFile bool - FileExt string } type Flag struct { - // Name is the actual name as it appears on the CLI without the dash. - Name string // AutoComplete specifies if the flag has any auto complete options. AutoComplete *AutoComplete + // Name is the actual name as it appears on the CLI without the dash. + Name string } // FlagSet is an extension of [flag.FlagSet] that enables auto completion providers for a command. type FlagSet struct { *flag.FlagSet - flags []Flag - nameToAc map[string]*AutoComplete - - wantsFile bool + nameToAc map[string]*AutoComplete fileExt string + flags []Flag + wantsFile bool } // NewAutoCompleteFlagSet wraps a [flag.FlagSet] with autocomplete configuration, if [wantsFile] is set then diff --git a/graph/data/data.go b/graph/data/data.go index a2d5e54..4f4b05b 100644 --- a/graph/data/data.go +++ b/graph/data/data.go @@ -20,13 +20,13 @@ import ( ) type Data struct { - URL string Header *Header Network *Network + Runs *Runs + URL string InsertOrder []DataIndexes Blocks []*Block TotalCount int64 - Runs *Runs PingsMeta version } diff --git a/graph/data/data_test.go b/graph/data/data_test.go index 9e43bf7..2cbfe91 100644 --- a/graph/data/data_test.go +++ b/graph/data/data_test.go @@ -206,16 +206,15 @@ type BlockTest struct { } type DataTestCase struct { - Values []ping.PingResults - BlockSize int + ExpectedRuns data.Runs + BlockTest *BlockTest ExpectedGraphSpan data.TimeSpan + ExpectedSummary string + Values []ping.PingResults ExpectedGraphStats data.Stats + BlockSize int ExpectedPacketLoss float64 ExpectedTotalCount int - ExpectedRuns data.Runs - BlockTest *BlockTest - - ExpectedSummary string } // A fixed time stamp to make all testing relative too diff --git a/graph/data/serialisation_test.go b/graph/data/serialisation_test.go index 51cabe3..e98cbf5 100644 --- a/graph/data/serialisation_test.go +++ b/graph/data/serialisation_test.go @@ -170,9 +170,9 @@ func testCompacter(t th.T, start, empty data.Compact) { } type FileTest struct { + tz *time.Location FileName string ExpectedSummary string - tz *time.Location } func (ft FileTest) Run(t *testing.T) { diff --git a/graph/draw_window.go b/graph/draw_window.go index 3e96d49..3bdc1e9 100644 --- a/graph/draw_window.go +++ b/graph/draw_window.go @@ -62,12 +62,11 @@ const ( ) type label struct { + symbol string + text string coords - - symbol string - text string - leftJustify bool colour colour + leftJustify bool } type colour int diff --git a/graph/drawing.go b/graph/drawing.go index 2b82d79..97f4484 100644 --- a/graph/drawing.go +++ b/graph/drawing.go @@ -163,10 +163,10 @@ func translate(p ping.PingDataPoint, x *XAxisSpanInfo, y drawingYAxis, s termina } type gradientState struct { + lastGoodSpan *XAxisSpanInfo lastGoodIndex int64 lastGoodTerminalWidth int lastGoodTerminalHeight int - lastGoodSpan *XAxisSpanInfo } func (g gradientState) set(i int64, x, y int, s *XAxisSpanInfo) gradientState { diff --git a/graph/graph.go b/graph/graph.go index cb7cbe4..8fc3600 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -25,26 +25,18 @@ import ( ) type Graph struct { - Term *terminal.Terminal - - ui gui.GUI - - debugStrict bool - - sinkAlive bool - dataChannel <-chan ping.PingResults - - initial ping.PingsPerMinute - - data *graphdata.GraphData - - frameMutex *sync.Mutex - lastFrame frame - - drawingBuffer *draw.Buffer - + ui gui.GUI + Term *terminal.Terminal + dataChannel <-chan ping.PingResults + data *graphdata.GraphData + frameMutex *sync.Mutex + drawingBuffer *draw.Buffer presentation *controlState controlChannel <-chan Control + lastFrame frame + initial ping.PingsPerMinute + debugStrict bool + sinkAlive bool } // Control is the signal type which changes the presentation of the graph. @@ -54,8 +46,8 @@ type Control struct { } type Change[T any] struct { - DidChange bool Value T + DidChange bool } // Presentation is the dynamic part of the graph, these only change the presentation of the graph when drawn @@ -69,23 +61,22 @@ type Presentation struct { } type GraphConfiguration struct { + // Gui is the external GUI interface which the terminal will call [gui.Draw] on each frame if the gui + // component requires it. + Gui gui.GUI // Input will be owned by the graph and represents the source of data for the graph to plot. Input <-chan ping.PingResults // Terminal is the underlying terminal the graph will draw to and perform all external I/O too. The graph // takes ownership of the terminal. - Terminal *terminal.Terminal - // Gui is the external GUI interface which the terminal will call [gui.Draw] on each frame if the gui - // component requires it. - Gui gui.GUI - PingsPerMinute ping.PingsPerMinute + Terminal *terminal.Terminal + DrawingBuffer *draw.Buffer + ControlPlane <-chan Control + // Optional (can be nil) + Data *data.Data URL string - DrawingBuffer *draw.Buffer + PingsPerMinute ping.PingsPerMinute Presentation Presentation - ControlPlane <-chan Control DebugStrict bool - - // Optionals: - Data *data.Data } func StartUp() { @@ -288,13 +279,13 @@ func (g *Graph) checkf(shouldBeTrue bool, format string, a ...any) { } type frame struct { - PacketCount int64 - yAxis drawingYAxis xAxis drawingXAxis + spinnerData spinner framePainter func(io.Writer) error framePainterNoGui func(io.Writer) error - spinnerData spinner + yAxis drawingYAxis cfg computeFrameConfig + PacketCount int64 } func (f frame) Match(s terminal.Size, cfg computeFrameConfig) bool { @@ -307,7 +298,6 @@ func (f frame) Size() terminal.Size { } type controlState struct { - Presentation - m *sync.Mutex + Presentation } diff --git a/graph/graph_file_test.go b/graph/graph_file_test.go index d54df15..ddd4abb 100644 --- a/graph/graph_file_test.go +++ b/graph/graph_file_test.go @@ -39,11 +39,11 @@ const ( ) type FileTest struct { + TimeZoneOfFile *time.Location FileName string Sizes []terminal.Size - OnlyDoLinear bool - TimeZoneOfFile *time.Location TerminalWrapping th.TerminalWrapping + OnlyDoLinear bool } var StandardTestSizes = []terminal.Size{ diff --git a/graph/graph_test.go b/graph/graph_test.go index 4c08ba8..bb7df70 100644 --- a/graph/graph_test.go +++ b/graph/graph_test.go @@ -189,9 +189,9 @@ func TestThousandsDrawing(t *testing.T) { } type DrawingTest struct { - Size terminal.Size - Values []ping.PingDataPoint ExpectedFile string + Values []ping.PingDataPoint + Size terminal.Size } //nolint:unused diff --git a/graph/graphdata/graphdata.go b/graph/graphdata/graphdata.go index 3117905..7e0b5e9 100644 --- a/graph/graphdata/graphdata.go +++ b/graph/graphdata/graphdata.go @@ -25,9 +25,9 @@ import ( type GraphData struct { data *data.Data + m *sync.Mutex spans []*SpanInfo spanIndex int - m *sync.Mutex } func NewGraphData(d *data.Data) *GraphData { @@ -106,9 +106,9 @@ func (gd *GraphData) LockFreeIter(followLatestSpan bool) *Iter { } type Iter struct { - Total int64 d *data.Data spans Spans + Total int64 offset int64 } diff --git a/graph/graphdata/graphdata_test.go b/graph/graphdata/graphdata_test.go index 7ed4aef..d504eba 100644 --- a/graph/graphdata/graphdata_test.go +++ b/graph/graphdata/graphdata_test.go @@ -221,8 +221,8 @@ func (test TimeSpanTest) Run(t *testing.T) { type TimeSpanFileTest struct { File string - ExpectedSpanCount int ExpectedSpans []*data.TimeSpan + ExpectedSpanCount int } func (test TimeSpanFileTest) Run(t *testing.T) { diff --git a/graph/spinner.go b/graph/spinner.go index 3240d9d..a282e3d 100644 --- a/graph/spinner.go +++ b/graph/spinner.go @@ -23,8 +23,8 @@ var spinnerArray = [...]string{ } type spinner struct { - spinnerIndex int timestampLastDrawn time.Time + spinnerIndex int } func (s *spinner) spinner(size terminal.Size) string { diff --git a/graph/xAxis.go b/graph/xAxis.go index 9242ede..a4380f6 100644 --- a/graph/xAxis.go +++ b/graph/xAxis.go @@ -23,19 +23,19 @@ import ( ) type XAxisSpanInfo struct { - spans []*graphdata.SpanInfo spanStats *data.Stats pingStats *data.Stats timeSpan *data.TimeSpan + spans []*graphdata.SpanInfo startX int endX int width int } type drawingXAxis struct { - size int - spans []*XAxisSpanInfo overallSpan *data.TimeSpan + spans []*XAxisSpanInfo + size int } type xAxisIter struct { diff --git a/graph/yAxis.go b/graph/yAxis.go index 1742dfd..71f972c 100644 --- a/graph/yAxis.go +++ b/graph/yAxis.go @@ -22,8 +22,8 @@ import ( ) type drawingYAxis struct { - size int stats *data.Stats + size int labelSize int scale YAxisScale } diff --git a/gui/box_test.go b/gui/box_test.go index a5fccab..cc49176 100644 --- a/gui/box_test.go +++ b/gui/box_test.go @@ -214,11 +214,11 @@ func (tc *testCase) getOutputFileName() string { } type testCase struct { + text []string + position gui.Position size terminal.Size style gui.Style - position gui.Position align gui.HorizontalAlignment - text []string result hash } diff --git a/gui/notifications.go b/gui/notifications.go index 61f667b..5596f6b 100644 --- a/gui/notifications.go +++ b/gui/notifications.go @@ -25,9 +25,9 @@ import ( type Notification[T any] struct { m *sync.Mutex storage map[int]stored[T] - key int drawFunc func(terminal.Size, []T) Draw lastSize terminal.Size + key int } // NewNotification builds a new thread safe collection of [T]. The function passed will be called with a diff --git a/gui/themes/colours_test.go b/gui/themes/colours_test.go index 43e819f..be7e0aa 100644 --- a/gui/themes/colours_test.go +++ b/gui/themes/colours_test.go @@ -48,9 +48,9 @@ green component out of range -3, should be within 0 and 255`)}, } type CSSTest struct { + Err error input string R, G, B uint8 - Err error } func (tc CSSTest) Run(t *testing.T) { diff --git a/gui/themes/themes_test.go b/gui/themes/themes_test.go index 8f2fa00..6fda38f 100644 --- a/gui/themes/themes_test.go +++ b/gui/themes/themes_test.go @@ -86,10 +86,10 @@ func CurryTrueColour(r, g, b uint8) func(s string) string { } type AnsiThemeTest struct { - Input string - Output func(s string) string UnmarshalErr error ImplErr error + Output func(s string) string + Input string } const testString = "abcdefghijklmnopqrstuvwxyz" diff --git a/ping/addr.go b/ping/addr.go index 02b18ae..86095f5 100644 --- a/ping/addr.go +++ b/ping/addr.go @@ -9,10 +9,9 @@ package ping import "net" type addr struct { - ip net.IP - asUDP *net.UDPAddr asIP *net.IPAddr + ip net.IP } func New(addrType addressType, ip net.IP) *addr { diff --git a/ping/api.go b/ping/api.go index c94c949..c9548ea 100644 --- a/ping/api.go +++ b/ping/api.go @@ -22,16 +22,15 @@ import ( ) type Ping struct { + echoType icmp.Type + echoReply icmp.Type connect *icmp.PacketConn - addrType addressType - id uint16 + addresses *queryCache currentURL string + addrType addressType timeout time.Duration ratelimitTime time.Duration - - echoType, echoReply icmp.Type - - addresses *queryCache + id uint16 } // NewPing constructs a new Ping client which can perform accurate ping measurements. Either with @@ -183,20 +182,20 @@ func (p *Ping) CreateFlexibleChannel( } type PingResults struct { + // InternalErr represents some problem with [ping] package internal state which didn't gracefully handle + // some network problem. Other network problems which are expected and represent dropped packets **should + // be** handled gracefully and will be reported in the [PingDataPoint] field in the [Dropped]. + InternalErr error // Data is the data about this ping, containing the time taken for round trip or details if the packet was // dropped. Data PingDataPoint // IP is the address which this ping result was achieved from. IP net.IP - // InternalErr represents some problem with [ping] package internal state which didn't gracefully handle - // some network problem. Other network problems which are expected and represent dropped packets **should - // be** handled gracefully and will be reported in the [PingDataPoint] field in the [Dropped]. - InternalErr error } type PingDataPoint struct { - Duration time.Duration Timestamp time.Time + Duration time.Duration DropReason Dropped } diff --git a/ping/api_implementation.go b/ping/api_implementation.go index 347099a..e8280cd 100644 --- a/ping/api_implementation.go +++ b/ping/api_implementation.go @@ -198,8 +198,8 @@ func (pt pingTimeout) Error() string { return "PingTimeout {" + pt.String() + "} func (p *Ping) pingRead(ctx context.Context, buffer []byte) (int, error) { type read struct { - n int err error + n int } c := make(chan read) go func() { @@ -301,9 +301,8 @@ var listenList = []listenerConfig{ } type listenerConfig struct { - addressType - network, address string + addressType } type addressType int diff --git a/terminal/parser.go b/terminal/parser.go index 317e1e6..c8aff5f 100644 --- a/terminal/parser.go +++ b/terminal/parser.go @@ -142,10 +142,9 @@ type parser struct { Ctx context.Context ToStreamFrom io.Reader Buffer []byte - - parserHead int - pointer int - bufferSlice []byte + bufferSlice []byte + parserHead int + pointer int } // Consume the exact bytes passed, errors if the stream produced different bytes. diff --git a/terminal/terminal.go b/terminal/terminal.go index d7c5174..17dd847 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -14,7 +14,6 @@ import ( "slices" "strconv" "strings" - "sync" "github.com/Lexer747/acci-ping/gui/themes" "github.com/Lexer747/acci-ping/terminal/ansi" @@ -57,27 +56,20 @@ func ParseSize(s string) (Size, bool) { // - [NewParsedFixedSizeTerminal] // - [NewTestTerminal] type Terminal struct { - size atomic.Of[Size] - listeners []ConditionalListener - fallbacks []Listener - - stdin io.ReadWriter - stdout io.ReadWriter - - stdinFd int + stdin io.ReadWriter + stdout io.ReadWriter terminalSizeCallBack func() (Size, error) - - isTestTerminal bool - isDynamicSize bool - neverRaw bool - + // should be called if a panic occurs otherwise stacktraces are unreadable + cleanup func() + fallbacks []Listener + listeners []ConditionalListener + size atomic.Of[Size] + stdinFd int backgroundColour themes.Luminance backgroundDiscovered backgroundDiscovered - - // should be called if a panic occurs otherwise stacktraces are unreadable - cleanup func() - - listenMutex *sync.Mutex + isTestTerminal bool + isDynamicSize bool + neverRaw bool } // NewTerminal creates a new terminal which immediately tries to verify that it can operate for @@ -99,7 +91,6 @@ func NewTerminal() (*Terminal, error) { fallbacks: []Listener{}, stdin: os.Stdin, stdout: os.Stdout, - listenMutex: &sync.Mutex{}, isDynamicSize: true, stdinFd: int(os.Stdin.Fd()), terminalSizeCallBack: func() (Size, error) { @@ -126,7 +117,6 @@ func NewFixedSizeTerminal(s Size) (*Terminal, error) { fallbacks: []Listener{}, stdin: os.Stdin, stdout: os.Stdout, - listenMutex: &sync.Mutex{}, isDynamicSize: false, } return t, nil @@ -139,7 +129,6 @@ func NewDebuggingTerminal(s Size) (*Terminal, error) { fallbacks: []Listener{}, stdin: os.Stdin, stdout: os.Stdout, - listenMutex: &sync.Mutex{}, isDynamicSize: false, neverRaw: true, } @@ -183,7 +172,6 @@ func NewTestTerminal(stdinReader, stdoutWriter io.ReadWriter, terminalSizeCallBa terminalSizeCallBack: func() (Size, error) { return terminalSizeCallBack(), nil }, isTestTerminal: true, isDynamicSize: true, - listenMutex: &sync.Mutex{}, }, nil } @@ -204,21 +192,20 @@ func (t *Terminal) UpdateSize() error { } type Listener struct { - // Name is used for if a listener errors for easier identification, it may be omitted. - Name string // Action the callback which will be invoked when a user inputs the applicable rune, the rune passed is // the same rune passed to applicable. Note the terminal size will have been updated before this called, // but this is actually racey if the user is typing while changing size. If an error occurs in this action // the terminal will panic and exit. Action func(rune) error + // Name is used for if a listener errors for easier identification, it may be omitted. + Name string } type ConditionalListener struct { - Listener - // Applicable is the applicability of this listen, i.e. for which input runes do you want this action to // be fired Applicable func(rune) bool + Listener } type userControlCErr struct{} @@ -358,8 +345,8 @@ const ( ) type listenResult struct { - n int err error + n int } func (t *Terminal) beingListening(ctx context.Context) { diff --git a/tools/fieldalignment-lint.sh b/tools/fieldalignment-lint.sh new file mode 100755 index 0000000..298aca0 --- /dev/null +++ b/tools/fieldalignment-lint.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +ROOT=$(git rev-parse --show-toplevel) +pushd "$ROOT" &> /dev/null || exit + + +fieldalignment ./... &> /dev/null +exitCode=$? +if [[ $exitCode != 0 ]]; then + fieldalignment -fix -diff ./... +else + echo "fieldalignment good :)" +fi + +popd &> /dev/null || exit +exit $exitCode diff --git a/tools/verify.sh b/tools/verify.sh index 1d3f20f..ff02728 100755 --- a/tools/verify.sh +++ b/tools/verify.sh @@ -32,4 +32,5 @@ if [ $testsExitCode -eq 0 ] || [[ "$1" == "update" ]]; then find . -type d -empty -delete fi -"$ROOT"/tools/sub-command-test.sh \ No newline at end of file +"$ROOT"/tools/sub-command-test.sh +"$ROOT"/tools/fieldalignment-lint.sh \ No newline at end of file diff --git a/utils/numeric/numeric_test.go b/utils/numeric/numeric_test.go index 5073ddc..755ab60 100644 --- a/utils/numeric/numeric_test.go +++ b/utils/numeric/numeric_test.go @@ -21,10 +21,12 @@ import ( func TestNormalize(t *testing.T) { t.Parallel() type Case struct { - Min, Max float64 - NewMin, NewMax float64 - Inputs []float64 - Expected []float64 + Inputs []float64 + Expected []float64 + Min float64 + Max float64 + NewMin float64 + NewMax float64 } cases := []Case{ { diff --git a/utils/sliceutils/sliceutils_test.go b/utils/sliceutils/sliceutils_test.go index 08d6abc..5c8b411 100644 --- a/utils/sliceutils/sliceutils_test.go +++ b/utils/sliceutils/sliceutils_test.go @@ -67,8 +67,8 @@ func TestSplitN(t *testing.T) { type testCase[T comparable] struct { Input []T - SplitN int Output [][]T + SplitN int } func (tc testCase[T]) Run(t *testing.T) { diff --git a/utils/th/helper_debug.go b/utils/th/helper_debug.go index f469ad1..6c6c723 100644 --- a/utils/th/helper_debug.go +++ b/utils/th/helper_debug.go @@ -17,8 +17,8 @@ type debug struct { } type debugItem struct { - sequence []rune command string + sequence []rune startIndex int endIndex int } diff --git a/utils/th/test_helper.go b/utils/th/test_helper.go index abae937..50bc782 100644 --- a/utils/th/test_helper.go +++ b/utils/th/test_helper.go @@ -127,20 +127,19 @@ func EmulateTerminal(ansiText string, buffer []string, size terminal.Size, termi } type ansiState struct { - cursorRow, cursorColumn int + ansiText string // buffer is the in memory representation of the terminal, each entry of the slice is a row. - buffer []string - size terminal.Size - ansiText string - asRunes []rune - head int - outOfBounds bool - - terminalWrapping TerminalWrapping - + buffer []string + asRunes []rune // for errors in tests we accumulate this debug object which will store the runes processed to create the // current terminal. - debug debug + debug debug + size terminal.Size + cursorRow int + cursorColumn int + head int + terminalWrapping TerminalWrapping + outOfBounds bool } func (a *ansiState) peekN(n int) rune { return a.asRunes[a.head+n] }