charmbracelet / charmbracelet/x

bug(ansi): Truncate, TruncateLeft etc do not obey length in many cases

Open
#541 1 comment 2 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
314
Forks
94
Avg merge
3d 22h
Merged PRs (30d)
2

Description

### Describe the bug

In many cases, `Truncate`, `TruncateLeft`, and potentially other functions do not obey the provided length, at least lengths that get returned by `StringWidth`.

### To Reproduce

Use the following patch to add fuzzing for `Truncate` and `TruncateLeft`:

```patch
diff --git a/ansi/truncate_test.go b/ansi/truncate_test.go
index decf11a..17e5ba3 100644
--- a/ansi/truncate_test.go
+++ b/ansi/truncate_test.go
@@ -1,6 +1,7 @@
package ansi

import (
+ "math/rand"
"testing"
)

@@ -458,3 +459,35 @@ func TestByteToGraphemeRange(t *testing.T) {
})
}
}
+
+func FuzzTruncate(f *testing.F) {
+ for _, tc := range tcases {
+ f.Add(tc.input)
+ }
+
+ f.Fuzz(func(t *testing.T, input string) {
+ n := rand.Intn(100) + 1
+ result := Truncate(input, n, "…")
+ w := StringWidth(result)
+
+ if w > n {
+ t.Errorf("expected width to be no greater than %d, got %d", n, w)
+ }
+ })
+}
+
+func FuzzTruncateLeft(f *testing.F) {
+ for _, tc := range tcases {
+ f.Add(tc.input)
+ }
+
+ f.Fuzz(func(t *testing.T, input string) {
+ n := rand.Intn(100) + 1
+ result := TruncateLeft(input, n, "…")
+ w := StringWidth(result)
+
+ if w > n {
+ t.Errorf("expected width to be no greater than %d, got %d", n, w)
+ }
+ })
+}
```

And run with:

```console
$ go test -run=".*" -fuzz="^FuzzTruncate$" ./ansi/
# or
$ go test -run=".*" -fuzz="^FuzzTruncateLeft$" ./ansi/
```

I get failures nearly instantly. An example of 1:

```
string("\x9f \xec؋\xf00")
```

### Expected behavior

The above fuzzing methods simply check to see if the resulting output from either function is ever **longer** (according to `ansi.StringWidth()`) than the provided max length passed to the associated function. I would expect that the result to always be truncated and **never exceed the length provided as the input**, at least according to the "length" that `ansi.StringWidth()` also calculates.

Contributor guide

Open the contributing guide

Research direction

Start in ansi/truncate_test.go and run the provided Go fuzz commands for FuzzTruncate and FuzzTruncateLeft to reproduce the width failures. Trace Truncate and TruncateLeft from those tests, then verify that fuzz-generated results never have StringWidth greater than the supplied length for either function.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.