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
12 changes: 6 additions & 6 deletions cooldown.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)

Expand Down Expand Up @@ -50,17 +51,16 @@ type Config struct {
defaultDuration time.Duration
ecosystemDurations map[string]time.Duration
packageDurations map[string]time.Duration
parsed bool
parseOnce sync.Once
}

// parse resolves all string durations into time.Duration values.
// Called lazily on first use.
// Called lazily on first use, once even when the first calls are concurrent.
func (c *Config) parse() {
if c.parsed {
return
}
c.parsed = true
c.parseOnce.Do(c.parseDurations)
}

func (c *Config) parseDurations() {
c.defaultDuration, _ = ParseDuration(c.Default)

c.ecosystemDurations = make(map[string]time.Duration, len(c.Ecosystems))
Expand Down
42 changes: 36 additions & 6 deletions cooldown_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cooldown

import (
"sync"
"testing"
"time"
)
Expand Down Expand Up @@ -198,14 +199,14 @@ func TestConfigEvaluate(t *testing.T) {
func TestConfigEnabled(t *testing.T) {
tests := []struct {
name string
cfg Config
cfg *Config
want bool
}{
{"empty config", Config{}, false},
{"default only", Config{Default: "3d"}, true},
{"ecosystem only", Config{Ecosystems: map[string]string{"npm": "7d"}}, true},
{"package only", Config{Packages: map[string]string{"pkg:npm/x": "1d"}}, true},
{"all zero", Config{Default: "0", Ecosystems: map[string]string{"npm": "0"}}, false},
{"empty config", &Config{}, false},
{"default only", &Config{Default: "3d"}, true},
{"ecosystem only", &Config{Ecosystems: map[string]string{"npm": "7d"}}, true},
{"package only", &Config{Packages: map[string]string{"pkg:npm/x": "1d"}}, true},
{"all zero", &Config{Default: "0", Ecosystems: map[string]string{"npm": "0"}}, false},
}

for _, tt := range tests {
Expand All @@ -217,3 +218,32 @@ func TestConfigEnabled(t *testing.T) {
})
}
}

func TestConfigConcurrentFirstUse(t *testing.T) {
for range 50 {
c := &Config{
Default: "3d",
Ecosystems: map[string]string{"npm": "7d"},
Packages: map[string]string{"pkg:npm/lodash": "1d"},
}
const n = 8
got := make([]time.Duration, n)
var start, wg sync.WaitGroup
start.Add(1)
for i := range n {
wg.Add(1)
go func() {
defer wg.Done()
start.Wait()
got[i] = c.For("npm", "pkg:npm/express")
}()
}
start.Done()
wg.Wait()
for i, d := range got {
if d != 7*24*time.Hour {
t.Fatalf("goroutine %d: For = %v, want 168h", i, d)
}
}
}
}
Loading