Skip to content

Commit d40880e

Browse files
authored
Separate workflow title parsing from file-I/O benchmark costs (#71927)
1 parent 32ecafb commit d40880e

2 files changed

Lines changed: 107 additions & 2 deletions

File tree

‎Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ bench-performance:
206206
-benchmem -benchtime=2s -run=^$$ ./pkg/workflow | tee bench_performance.txt
207207
@echo ""
208208
@echo "Also running CLI helper benchmarks..."
209-
@go test -bench='Benchmark(ExtractWorkflowNameFromFile|FindIncludesInContent)$$' \
209+
@go test -bench='Benchmark(ExtractWorkflowNameFromFile|FastParseTitleFromReader|FindIncludesInContent)$$' \
210210
-benchmem -benchtime=2s -run=^$$ ./pkg/cli >> bench_performance.txt
211211
@echo ""
212212
@echo "Performance benchmark results saved to bench_performance.txt"

‎pkg/cli/commands_utils_test.go‎

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ package cli
44

55
import (
66
"bufio"
7+
"errors"
8+
"io"
79
"os"
810
"path/filepath"
911
"strings"
1012
"testing"
13+
"testing/iotest"
1114

1215
"github.com/github/gh-aw/pkg/testutil"
1316
)
@@ -170,6 +173,90 @@ func TestExtractWorkflowNameFromFile_LargeFrontmatterLine(t *testing.T) {
170173
}
171174
}
172175

176+
func TestFastParseTitleFromReader(t *testing.T) {
177+
t.Parallel()
178+
tests := []struct {
179+
name string
180+
content string
181+
expected string
182+
expectError bool
183+
}{
184+
{
185+
name: "first supported heading wins",
186+
content: "## First heading\n# Later heading\n",
187+
expected: "First heading",
188+
},
189+
{
190+
name: "frontmatter headings are ignored",
191+
content: "---\n# Not a title\n---\n### Actual title",
192+
expected: "Actual title",
193+
},
194+
{
195+
name: "unicode whitespace and CRLF",
196+
content: "\u2003---\u2003\r\nkey: value\r\n---\r\n\u2003# Title\u2003\r\n",
197+
expected: "Title",
198+
},
199+
{
200+
name: "empty heading stops parsing",
201+
content: "# \n# Later heading\n",
202+
expected: "",
203+
},
204+
{
205+
name: "frontmatter only opens on the first line",
206+
content: "\n---\n# Title\n",
207+
expected: "Title",
208+
},
209+
{
210+
name: "unclosed frontmatter",
211+
content: "---\n# Not a title\n",
212+
expectError: true,
213+
},
214+
{
215+
name: "oversized line",
216+
content: strings.Repeat("x", 1024*1024) + "\n# Title\n",
217+
expectError: true,
218+
},
219+
}
220+
for _, tt := range tests {
221+
t.Run(tt.name, func(t *testing.T) {
222+
title, err := fastParseTitleFromReader(strings.NewReader(tt.content))
223+
if (err != nil) != tt.expectError {
224+
t.Fatalf("Expected error=%v, got %v", tt.expectError, err)
225+
}
226+
if title != tt.expected {
227+
t.Errorf("Expected title %q, got %q", tt.expected, title)
228+
}
229+
})
230+
}
231+
}
232+
233+
func TestFastParseTitleFromReader_ReadError(t *testing.T) {
234+
t.Parallel()
235+
readErr := errors.New("unexpected read")
236+
for _, tt := range []struct {
237+
name string
238+
content string
239+
expected string
240+
}{
241+
{name: "before title"},
242+
{name: "after title", content: "# Title\n", expected: "Title"},
243+
} {
244+
t.Run(tt.name, func(t *testing.T) {
245+
reader := io.MultiReader(strings.NewReader(tt.content), iotest.ErrReader(readErr))
246+
title, err := fastParseTitleFromReader(reader)
247+
if tt.expected == "" && !errors.Is(err, readErr) {
248+
t.Fatalf("Expected read error, got %v", err)
249+
}
250+
if tt.expected != "" && err != nil {
251+
t.Fatalf("Parser read past the title: %v", err)
252+
}
253+
if title != tt.expected {
254+
t.Errorf("Expected title %q, got %q", tt.expected, title)
255+
}
256+
})
257+
}
258+
}
259+
173260
func TestIsGitRepo(t *testing.T) {
174261
t.Parallel()
175262
// Test in current directory (should be a git repo based on project setup)
@@ -362,6 +449,21 @@ func TestFindIncludesInContent_EmptyContentReturnsNonNilSlice(t *testing.T) {
362449
}
363450

364451
// Benchmark tests
452+
// Measure parsing separately from file I/O to distinguish parser regressions
453+
// from filesystem or runner overhead in BenchmarkExtractWorkflowNameFromFile.
454+
func BenchmarkFastParseTitleFromReader(b *testing.B) {
455+
const content = "---\ntitle: Test Workflow\n---\n\n# Daily Test Coverage Improvement\n\nThis is a test workflow with some content."
456+
reader := strings.NewReader(content)
457+
b.ReportAllocs()
458+
for b.Loop() {
459+
reader.Reset(content)
460+
title, err := fastParseTitleFromReader(reader)
461+
if err != nil || title != "Daily Test Coverage Improvement" {
462+
b.Fatalf("Unexpected title %q or error: %v", title, err)
463+
}
464+
}
465+
}
466+
365467
func BenchmarkExtractWorkflowNameFromFile(b *testing.B) {
366468
// Create temporary test file
367469
tmpDir := b.TempDir()
@@ -380,7 +482,10 @@ This is a test workflow with some content.`
380482
}
381483

382484
for b.Loop() {
383-
_, _ = extractWorkflowNameFromFile(filePath)
485+
title, err := extractWorkflowNameFromFile(filePath)
486+
if err != nil || title != "Daily Test Coverage Improvement" {
487+
b.Fatalf("Unexpected title %q or error: %v", title, err)
488+
}
384489
}
385490
}
386491

0 commit comments

Comments
 (0)