mirror of
https://github.com/aykhans/dodo.git
synced 2025-09-03 18:03:34 +00:00
Compare commits
24 Commits
v0.7.1
...
25d4762a3c
Author | SHA1 | Date | |
---|---|---|---|
25d4762a3c | |||
361d423651 | |||
ffa724fae7 | |||
7930be490d | |||
e6c54e9cb2 | |||
b32f567de7 | |||
b6e85d9443 | |||
827e3535cd | |||
7ecf534d87 | |||
![]() |
17ad5fadb9 | ||
7fb59a7989 | |||
527909c882 | |||
4459675efa | |||
![]() |
604af355e6 | ||
7d4267c4c2 | |||
![]() |
845ab7296c | ||
49d004ff06 | |||
045deb6120 | |||
075ef26203 | |||
![]() |
946afbb2c3 | ||
aacb33cfa5 | |||
4a7db48351 | |||
b73087dce5 | |||
![]() |
20a46feab8 |
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@@ -21,5 +21,5 @@ jobs:
|
|||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v7
|
uses: golangci/golangci-lint-action@v7
|
||||||
with:
|
with:
|
||||||
version: v2.0.2
|
version: v2.4.0
|
||||||
args: --timeout=10m --config=.golangci.yml
|
args: --timeout=10m --config=.golangci.yml
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
version: "2"
|
version: "2"
|
||||||
|
|
||||||
run:
|
run:
|
||||||
go: "1.24"
|
go: "1.25"
|
||||||
concurrency: 8
|
concurrency: 8
|
||||||
timeout: 10m
|
timeout: 10m
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.24-alpine AS builder
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<h1 align="center">Dodo - A Fast and Easy-to-Use HTTP Benchmarking Tool</h1>
|
<h1 align="center">Dodo - A Fast and Easy-to-Use HTTP Benchmarking Tool</h1>
|
||||||
<p align="center">
|
|
||||||
<img width="30%" height="30%" src="https://ftp.aykhans.me/web/client/pubshares/VzPtSHS7yPQT7ngoZzZSNU/browse?path=%2Fdodo.png">
|

|
||||||
</p>
|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h4>
|
<h4>
|
||||||
|
@@ -18,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VERSION string = "0.7.1"
|
VERSION string = "0.7.3"
|
||||||
DefaultUserAgent string = "Dodo/" + VERSION
|
DefaultUserAgent string = "Dodo/" + VERSION
|
||||||
DefaultMethod string = "GET"
|
DefaultMethod string = "GET"
|
||||||
DefaultTimeout time.Duration = time.Second * 10
|
DefaultTimeout time.Duration = time.Second * 10
|
||||||
@@ -159,9 +159,6 @@ func (config *Config) Validate() []error {
|
|||||||
if utils.IsNilOrZero(config.URL) {
|
if utils.IsNilOrZero(config.URL) {
|
||||||
errs = append(errs, errors.New("request URL is required"))
|
errs = append(errs, errors.New("request URL is required"))
|
||||||
} else {
|
} else {
|
||||||
if config.URL.Scheme == "" {
|
|
||||||
config.URL.Scheme = "http"
|
|
||||||
}
|
|
||||||
if config.URL.Scheme != "http" && config.URL.Scheme != "https" {
|
if config.URL.Scheme != "http" && config.URL.Scheme != "https" {
|
||||||
errs = append(errs, errors.New("request URL scheme must be http or https"))
|
errs = append(errs, errors.New("request URL scheme must be http or https"))
|
||||||
}
|
}
|
||||||
|
18
go.mod
18
go.mod
@@ -1,22 +1,22 @@
|
|||||||
module github.com/aykhans/dodo
|
module github.com/aykhans/dodo
|
||||||
|
|
||||||
go 1.24.2
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/brianvoe/gofakeit/v7 v7.2.1
|
github.com/brianvoe/gofakeit/v7 v7.3.0
|
||||||
github.com/jedib0t/go-pretty/v6 v6.6.7
|
github.com/jedib0t/go-pretty/v6 v6.6.8
|
||||||
github.com/valyala/fasthttp v1.62.0
|
github.com/valyala/fasthttp v1.65.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
golang.org/x/net v0.40.0 // indirect
|
golang.org/x/net v0.43.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.35.0 // indirect
|
||||||
golang.org/x/term v0.32.0 // indirect
|
golang.org/x/term v0.34.0 // indirect
|
||||||
golang.org/x/text v0.25.0 // indirect
|
golang.org/x/text v0.28.0 // indirect
|
||||||
)
|
)
|
||||||
|
32
go.sum
32
go.sum
@@ -1,11 +1,11 @@
|
|||||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
github.com/brianvoe/gofakeit/v7 v7.2.1 h1:AGojgaaCdgq4Adzrd2uWdbGNDyX6MWNhHdQBraNfOHI=
|
github.com/brianvoe/gofakeit/v7 v7.3.0 h1:TWStf7/lLpAjKw+bqwzeORo9jvrxToWEwp9b1J2vApQ=
|
||||||
github.com/brianvoe/gofakeit/v7 v7.2.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
|
github.com/brianvoe/gofakeit/v7 v7.3.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
|
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
|
||||||
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
@@ -19,18 +19,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.62.0 h1:8dKRBX/y2rCzyc6903Zu1+3qN0H/d2MsxPPmVNamiH0=
|
github.com/valyala/fasthttp v1.65.0 h1:j/u3uzFEGFfRxw79iYzJN+TteTJwbYkru9uDp3d0Yf8=
|
||||||
github.com/valyala/fasthttp v1.62.0/go.mod h1:FCINgr4GKdKqV8Q0xv8b+UxPV+H/O5nNFo3D+r54Htg=
|
github.com/valyala/fasthttp v1.65.0/go.mod h1:P/93/YkKPMsKSnATEeELUCkG8a7Y+k99uxNHVbKINr4=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
@@ -14,47 +14,30 @@ type Response struct {
|
|||||||
Time time.Duration
|
Time time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type Responses []*Response
|
type Responses []Response
|
||||||
|
|
||||||
// Print prints the responses in a tabular format, including information such as
|
// Print prints the responses in a tabular format, including information such as
|
||||||
// response count, minimum time, maximum time, average time, and latency percentiles.
|
// response count, minimum time, maximum time, average time, and latency percentiles.
|
||||||
func (responses Responses) Print() {
|
func (responses Responses) Print() {
|
||||||
total := struct {
|
if len(responses) == 0 {
|
||||||
Count int
|
return
|
||||||
Min time.Duration
|
|
||||||
Max time.Duration
|
|
||||||
Sum time.Duration
|
|
||||||
P90 time.Duration
|
|
||||||
P95 time.Duration
|
|
||||||
P99 time.Duration
|
|
||||||
}{
|
|
||||||
Count: len(responses),
|
|
||||||
Min: responses[0].Time,
|
|
||||||
Max: responses[0].Time,
|
|
||||||
}
|
}
|
||||||
mergedResponses := make(map[string]types.Durations)
|
|
||||||
var allDurations types.Durations
|
|
||||||
|
|
||||||
for _, response := range responses {
|
mergedResponses := make(map[string]types.Durations)
|
||||||
if response.Time < total.Min {
|
|
||||||
total.Min = response.Time
|
totalDurations := make(types.Durations, len(responses))
|
||||||
}
|
var totalSum time.Duration
|
||||||
if response.Time > total.Max {
|
totalCount := len(responses)
|
||||||
total.Max = response.Time
|
|
||||||
}
|
for i, response := range responses {
|
||||||
total.Sum += response.Time
|
totalSum += response.Time
|
||||||
|
totalDurations[i] = response.Time
|
||||||
|
|
||||||
mergedResponses[response.Response] = append(
|
mergedResponses[response.Response] = append(
|
||||||
mergedResponses[response.Response],
|
mergedResponses[response.Response],
|
||||||
response.Time,
|
response.Time,
|
||||||
)
|
)
|
||||||
allDurations = append(allDurations, response.Time)
|
|
||||||
}
|
}
|
||||||
allDurations.Sort()
|
|
||||||
allDurationsLenAsFloat := float64(len(allDurations) - 1)
|
|
||||||
total.P90 = allDurations[int(0.90*allDurationsLenAsFloat)]
|
|
||||||
total.P95 = allDurations[int(0.95*allDurationsLenAsFloat)]
|
|
||||||
total.P99 = allDurations[int(0.99*allDurationsLenAsFloat)]
|
|
||||||
|
|
||||||
t := table.NewWriter()
|
t := table.NewWriter()
|
||||||
t.SetOutputMirror(os.Stdout)
|
t.SetOutputMirror(os.Stdout)
|
||||||
@@ -93,15 +76,18 @@ func (responses Responses) Print() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(mergedResponses) > 1 {
|
if len(mergedResponses) > 1 {
|
||||||
|
totalDurations.Sort()
|
||||||
|
allDurationsLenAsFloat := float64(len(totalDurations) - 1)
|
||||||
|
|
||||||
t.AppendRow(table.Row{
|
t.AppendRow(table.Row{
|
||||||
"Total",
|
"Total",
|
||||||
total.Count,
|
totalCount,
|
||||||
utils.DurationRoundBy(total.Min, roundPrecision),
|
utils.DurationRoundBy(totalDurations[0], roundPrecision),
|
||||||
utils.DurationRoundBy(total.Max, roundPrecision),
|
utils.DurationRoundBy(totalDurations[len(totalDurations)-1], roundPrecision),
|
||||||
utils.DurationRoundBy(total.Sum/time.Duration(total.Count), roundPrecision), // Average
|
utils.DurationRoundBy(totalSum/time.Duration(totalCount), roundPrecision), // Average
|
||||||
utils.DurationRoundBy(total.P90, roundPrecision),
|
utils.DurationRoundBy(totalDurations[int(0.90*allDurationsLenAsFloat)], roundPrecision),
|
||||||
utils.DurationRoundBy(total.P95, roundPrecision),
|
utils.DurationRoundBy(totalDurations[int(0.95*allDurationsLenAsFloat)], roundPrecision),
|
||||||
utils.DurationRoundBy(total.P99, roundPrecision),
|
utils.DurationRoundBy(totalDurations[int(0.99*allDurationsLenAsFloat)], roundPrecision),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
t.Render()
|
t.Render()
|
||||||
|
@@ -66,7 +66,7 @@ func releaseDodos(
|
|||||||
streamWG sync.WaitGroup
|
streamWG sync.WaitGroup
|
||||||
requestCountPerDodo uint
|
requestCountPerDodo uint
|
||||||
dodosCount = requestConfig.GetValidDodosCountForRequests()
|
dodosCount = requestConfig.GetValidDodosCountForRequests()
|
||||||
responses = make([][]*Response, dodosCount)
|
responses = make([][]Response, dodosCount)
|
||||||
increase = make(chan int64, requestConfig.RequestCount)
|
increase = make(chan int64, requestConfig.RequestCount)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ func sendRequestByCount(
|
|||||||
request *Request,
|
request *Request,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
requestCount uint,
|
requestCount uint,
|
||||||
responseData *[]*Response,
|
responseData *[]Response,
|
||||||
increase chan<- int64,
|
increase chan<- int64,
|
||||||
wg *sync.WaitGroup,
|
wg *sync.WaitGroup,
|
||||||
) {
|
) {
|
||||||
@@ -146,7 +146,7 @@ func sendRequestByCount(
|
|||||||
if err == types.ErrInterrupt {
|
if err == types.ErrInterrupt {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
*responseData = append(*responseData, &Response{
|
*responseData = append(*responseData, Response{
|
||||||
Response: err.Error(),
|
Response: err.Error(),
|
||||||
Time: completedTime,
|
Time: completedTime,
|
||||||
})
|
})
|
||||||
@@ -154,7 +154,7 @@ func sendRequestByCount(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
*responseData = append(*responseData, &Response{
|
*responseData = append(*responseData, Response{
|
||||||
Response: strconv.Itoa(response.StatusCode()),
|
Response: strconv.Itoa(response.StatusCode()),
|
||||||
Time: completedTime,
|
Time: completedTime,
|
||||||
})
|
})
|
||||||
@@ -170,7 +170,7 @@ func sendRequest(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *Request,
|
request *Request,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
responseData *[]*Response,
|
responseData *[]Response,
|
||||||
increase chan<- int64,
|
increase chan<- int64,
|
||||||
wg *sync.WaitGroup,
|
wg *sync.WaitGroup,
|
||||||
) {
|
) {
|
||||||
@@ -193,7 +193,7 @@ func sendRequest(
|
|||||||
if err == types.ErrInterrupt {
|
if err == types.ErrInterrupt {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
*responseData = append(*responseData, &Response{
|
*responseData = append(*responseData, Response{
|
||||||
Response: err.Error(),
|
Response: err.Error(),
|
||||||
Time: completedTime,
|
Time: completedTime,
|
||||||
})
|
})
|
||||||
@@ -201,7 +201,7 @@ func sendRequest(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
*responseData = append(*responseData, &Response{
|
*responseData = append(*responseData, Response{
|
||||||
Response: strconv.Itoa(response.StatusCode()),
|
Response: strconv.Itoa(response.StatusCode()),
|
||||||
Time: completedTime,
|
Time: completedTime,
|
||||||
})
|
})
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -14,9 +15,7 @@ func (d Durations) Sort(ascending ...bool) {
|
|||||||
return d[i] > d[j]
|
return d[i] > d[j]
|
||||||
})
|
})
|
||||||
} else { // Otherwise, sort in ascending order
|
} else { // Otherwise, sort in ascending order
|
||||||
sort.Slice(d, func(i, j int) bool {
|
slices.Sort(d)
|
||||||
return d[i] < d[j]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,8 +2,8 @@ package utils
|
|||||||
|
|
||||||
import "math/rand"
|
import "math/rand"
|
||||||
|
|
||||||
func Flatten[T any](nested [][]*T) []*T {
|
func Flatten[T any](nested [][]T) []T {
|
||||||
flattened := make([]*T, 0)
|
flattened := make([]T, 0)
|
||||||
for _, n := range nested {
|
for _, n := range nested {
|
||||||
flattened = append(flattened, n...)
|
flattened = append(flattened, n...)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user