Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ jobs:
- name: Run tests with race detector
env:
GOCACHE: ${{ runner.temp }}/go-build-race
run: go test -race -count=1 ./...
run: go test -race -shuffle=on -count=1 ./...

e2e:
name: Bats e2e
Expand Down
9 changes: 5 additions & 4 deletions internal/markdown/bare_links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ func TestRenderWrapsANonASCIILinkWhole(t *testing.T) {
// text with a link in every sentence; finding them must not scan to the paragraph's end
// once for each.
func TestFindBareLinksInALongUnspacedParagraphIsLinear(t *testing.T) {
const links = 20_000
const links = 20_000 / linearInputScale
for _, sentence := range []string{
"「mailto:jane@example.org」と",
"「mailto:jane@example.org?subject=資料」と",
Expand Down Expand Up @@ -619,8 +619,9 @@ func TestLinkifyURLsPastManyHyperlinksIsLinear(t *testing.T) {
// A run of text full of prefixes that never become a link is read once, not once per
// prefix, and a real link after them is still found.
func TestFindBareLinksPastRejectedPrefixesIsLinear(t *testing.T) {
const repeats = 50_000 / linearInputScale
for _, prefix := range []string{"https://", "www.", "mailto:", "https://a.b_", "mailto:a@b.co_"} {
run := strings.Repeat(prefix, 50_000) + " https://docs.example.com/room_list"
run := strings.Repeat(prefix, repeats) + " https://docs.example.com/room_list"
found := make(chan []bareLink, 1)
go func() {
links, _ := findBareLinks(run)
Expand All @@ -629,10 +630,10 @@ func TestFindBareLinksPastRejectedPrefixesIsLinear(t *testing.T) {
select {
case links := <-found:
if len(links) == 0 || links[len(links)-1].target != "https://docs.example.com/room_list" {
t.Errorf("%q×50000: links end %v, want the room list last", prefix, links[max(0, len(links)-1):])
t.Errorf("%q×%d: links end %v, want the room list last", prefix, repeats, links[max(0, len(links)-1):])
}
case <-time.After(linearBound):
t.Fatalf("%q×50000: findBareLinks did not finish within %v", prefix, linearBound)
t.Fatalf("%q×%d: findBareLinks did not finish within %v", prefix, repeats, linearBound)
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions internal/markdown/bound_race_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ import "time"
// linearBound is longer under the race detector, which runs this code several times
// slower; a quadratic run is slower by the same factor and still far past it.
const linearBound = 60 * time.Second

// linearInputScale shrinks the largest linear-time inputs under the race detector. Those
// tests scan one string on one goroutine, so the detector has nothing to find in them and
// only multiplies their cost (44s and 13s, against 2.7s and 0.8s without it). The
// full-size inputs run in every build without -race, where the bound is held.
const linearInputScale = 10
4 changes: 4 additions & 0 deletions internal/markdown/bound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ import "time"
// quadratic run it guards against: in one pass each takes well under a second, and the
// quadratic runs took tens of seconds.
const linearBound = 5 * time.Second

// linearInputScale divides the largest linear-time inputs. Without the race detector
// they run at full size: this build is where the linear-time guarantee is held.
const linearInputScale = 1
Loading