From 9248aab215848a08f5b8e2af084dd76536bd252c Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 30 Apr 2024 00:26:39 +0300 Subject: [PATCH 1/8] feat: add time template helpers Signed-off-by: Sergey --- .gitignore | 3 +- helpers/templates/convert_to_float.go | 41 +++++++++++ helpers/templates/humamize_duration_test.go | 79 +++++++++++++++++++++ helpers/templates/humanize_duration.go | 66 +++++++++++++++++ 4 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 helpers/templates/convert_to_float.go create mode 100644 helpers/templates/humamize_duration_test.go create mode 100644 helpers/templates/humanize_duration.go diff --git a/.gitignore b/.gitignore index a725465a..31e3ac6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -vendor/ \ No newline at end of file +vendor/ +.idea diff --git a/helpers/templates/convert_to_float.go b/helpers/templates/convert_to_float.go new file mode 100644 index 00000000..8c2e4f05 --- /dev/null +++ b/helpers/templates/convert_to_float.go @@ -0,0 +1,41 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package templates + +import ( + "fmt" + "strconv" + "time" +) + +func convertToFloat(i interface{}) (float64, error) { + switch v := i.(type) { + case float64: + return v, nil + case string: + return strconv.ParseFloat(v, 64) + case int: + return float64(v), nil + case uint: + return float64(v), nil + case int64: + return float64(v), nil + case uint64: + return float64(v), nil + case time.Duration: + return v.Seconds(), nil + default: + return 0, fmt.Errorf("can't convert %T to float", v) + } +} diff --git a/helpers/templates/humamize_duration_test.go b/helpers/templates/humamize_duration_test.go new file mode 100644 index 00000000..be76ae23 --- /dev/null +++ b/helpers/templates/humamize_duration_test.go @@ -0,0 +1,79 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package templates + +import ( + "testing" + +) + +func TestHumanizeDurationSecondsFloat64(t *testing.T) { + input := []float64{0, 1, 60, 3600, 86400, 86400 + 3600, -(86400*2 + 3600*3 + 60*4 + 5), 899.99} + expected := []string{"0s", "1s", "1m 0s", "1h 0m 0s", "1d 0h 0m 0s", "1d 1h 0m 0s", "-2d 3h 4m 5s", "14m 59s"} + + for index, value := range input { + result, err := HumanizeDuration(value) + require.NoError(t, err) + require.Equal(t, expected[index], result) + } +} + +func TestHumanizeDurationSubsecondAndFractionalSecondsFloat64(t *testing.T) { + input := []float64{.1, .0001, .12345, 60.1, 60.5, 1.2345, 12.345} + expected := []string{"100ms", "100us", "123.5ms", "1m 0s", "1m 0s", "1.234s", "12.35s"} + + for index, value := range input { + result, err := HumanizeDuration(value) + require.NoError(t, err) + require.Equal(t, expected[index], result) + } +} + +func TestHumanizeDurationErrorString(t *testing.T) { + _, err := HumanizeDuration("one") + require.Error(t, err) +} + +func TestHumanizeDurationSecondsString(t *testing.T) { + input := []string{"0", "1", "60", "3600", "86400"} + expected := []string{"0s", "1s", "1m 0s", "1h 0m 0s", "1d 0h 0m 0s"} + + for index, value := range input { + result, err := HumanizeDuration(value) + require.NoError(t, err) + require.Equal(t, expected[index], result) + } +} + +func TestHumanizeDurationSubsecondAndFractionalSecondsString(t *testing.T) { + input := []string{".1", ".0001", ".12345", "60.1", "60.5", "1.2345", "12.345"} + expected := []string{"100ms", "100us", "123.5ms", "1m 0s", "1m 0s", "1.234s", "12.35s"} + + for index, value := range input { + result, err := HumanizeDuration(value) + require.NoError(t, err) + require.Equal(t, expected[index], result) + } +} + +func TestHumanizeDurationSecondsInt(t *testing.T) { + input := []int{0, -1, 1, 1234567} + expected := []string{"0s", "-1s", "1s", "14d 6h 56m 7s"} + + for index, value := range input { + result, err := HumanizeDuration(value) + require.NoError(t, err) + require.Equal(t, expected[index], result) + } +} diff --git a/helpers/templates/humanize_duration.go b/helpers/templates/humanize_duration.go new file mode 100644 index 00000000..2d68964a --- /dev/null +++ b/helpers/templates/humanize_duration.go @@ -0,0 +1,66 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package templates + +import ( + "fmt" + "math" +) + +func HumanizeDuration(i interface{}) (string, error) { + v, err := convertToFloat(i) + if err != nil { + return "", err + } + + if math.IsNaN(v) || math.IsInf(v, 0) { + return fmt.Sprintf("%.4g", v), nil + } + if v == 0 { + return fmt.Sprintf("%.4gs", v), nil + } + if math.Abs(v) >= 1 { + sign := "" + if v < 0 { + sign = "-" + v = -v + } + duration := int64(v) + seconds := duration % 60 + minutes := (duration / 60) % 60 + hours := (duration / 60 / 60) % 24 + days := duration / 60 / 60 / 24 + // For days to minutes, we display seconds as an integer. + if days != 0 { + return fmt.Sprintf("%s%dd %dh %dm %ds", sign, days, hours, minutes, seconds), nil + } + if hours != 0 { + return fmt.Sprintf("%s%dh %dm %ds", sign, hours, minutes, seconds), nil + } + if minutes != 0 { + return fmt.Sprintf("%s%dm %ds", sign, minutes, seconds), nil + } + // For seconds, we display 4 significant digits. + return fmt.Sprintf("%s%.4gs", sign, v), nil + } + prefix := "" + for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} { + if math.Abs(v) >= 1 { + break + } + prefix = p + v *= 1000 + } + return fmt.Sprintf("%.4g%ss", v, prefix), nil +} From 5bde41a53d80d4fb69db81f7a37257b7894528f1 Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 30 Apr 2024 00:37:19 +0300 Subject: [PATCH 2/8] chore: probably fixed CI Signed-off-by: Sergey --- go.mod | 3 +++ go.sum | 8 ++++++++ helpers/templates/humamize_duration_test.go | 1 + 3 files changed, 12 insertions(+) diff --git a/go.mod b/go.mod index 49377160..e7e9ef55 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,10 @@ require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/jpillora/backoff v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect @@ -28,6 +30,7 @@ require ( golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) retract v0.50.0 // Critical bug in counter suffixes, please read issue https://github.com/prometheus/common/issues/605 diff --git a/go.sum b/go.sum index 60ffee10..951e7da4 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,7 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= @@ -36,8 +37,13 @@ github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3c github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= @@ -56,4 +62,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/helpers/templates/humamize_duration_test.go b/helpers/templates/humamize_duration_test.go index be76ae23..ce0dd8ff 100644 --- a/helpers/templates/humamize_duration_test.go +++ b/helpers/templates/humamize_duration_test.go @@ -16,6 +16,7 @@ package templates import ( "testing" + "github.com/stretchr/testify/require" ) func TestHumanizeDurationSecondsFloat64(t *testing.T) { From c5c1b1de479eda6c80c698c1b0e7e99ac82ac59f Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 8 May 2024 20:32:37 +0300 Subject: [PATCH 3/8] chore: revert gitignore Signed-off-by: Sergey --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 31e3ac6d..a725465a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -vendor/ -.idea +vendor/ \ No newline at end of file From 270171b06674e76e940388385699e072173e2861 Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 8 May 2024 20:36:09 +0300 Subject: [PATCH 4/8] chore: fixed go.mod again Signed-off-by: Sergey --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index e7e9ef55..37dfa66d 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/prometheus/client_model v0.6.1 + github.com/stretchr/testify v1.8.2 golang.org/x/net v0.24.0 golang.org/x/oauth2 v0.19.0 google.golang.org/protobuf v1.34.0 From 80e630e1b2a9cbf81d221c17769a86b97b47a197 Mon Sep 17 00:00:00 2001 From: Sergey <83376337+freak12techno@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:58:07 +0300 Subject: [PATCH 5/8] Update helpers/templates/humamize_duration_test.go Co-authored-by: gotjosh Signed-off-by: Sergey <83376337+freak12techno@users.noreply.github.com> --- helpers/templates/humamize_duration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/templates/humamize_duration_test.go b/helpers/templates/humamize_duration_test.go index ce0dd8ff..426a5977 100644 --- a/helpers/templates/humamize_duration_test.go +++ b/helpers/templates/humamize_duration_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright 2024 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at From b9d8f7cedf3dfc973496b49e5ccb1be233192d45 Mon Sep 17 00:00:00 2001 From: Sergey <83376337+freak12techno@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:58:15 +0300 Subject: [PATCH 6/8] Update helpers/templates/humanize_duration.go Co-authored-by: gotjosh Signed-off-by: Sergey <83376337+freak12techno@users.noreply.github.com> --- helpers/templates/humanize_duration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/templates/humanize_duration.go b/helpers/templates/humanize_duration.go index 2d68964a..be53fac7 100644 --- a/helpers/templates/humanize_duration.go +++ b/helpers/templates/humanize_duration.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright 2024 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at From 4f7cf6ead1d7fd98f86316b82a18ec9a0559c968 Mon Sep 17 00:00:00 2001 From: Sergey <83376337+freak12techno@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:22:22 +0300 Subject: [PATCH 7/8] Update helpers/templates/convert_to_float.go Co-authored-by: gotjosh Signed-off-by: Sergey <83376337+freak12techno@users.noreply.github.com> --- helpers/templates/convert_to_float.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/templates/convert_to_float.go b/helpers/templates/convert_to_float.go index 8c2e4f05..b0102e98 100644 --- a/helpers/templates/convert_to_float.go +++ b/helpers/templates/convert_to_float.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright 2024 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at From 9f1953b45cd2a6bae3ef4c53799a9e4c69df09c5 Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 3 Jun 2024 13:33:20 +0300 Subject: [PATCH 8/8] chore: review fixes Signed-off-by: Sergey --- helpers/templates/convert_to_float.go | 41 ----- helpers/templates/humamize_duration_test.go | 80 ---------- .../{humanize_duration.go => time.go} | 23 +++ helpers/templates/time_test.go | 141 ++++++++++++++++++ 4 files changed, 164 insertions(+), 121 deletions(-) delete mode 100644 helpers/templates/convert_to_float.go delete mode 100644 helpers/templates/humamize_duration_test.go rename helpers/templates/{humanize_duration.go => time.go} (79%) create mode 100644 helpers/templates/time_test.go diff --git a/helpers/templates/convert_to_float.go b/helpers/templates/convert_to_float.go deleted file mode 100644 index b0102e98..00000000 --- a/helpers/templates/convert_to_float.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2024 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package templates - -import ( - "fmt" - "strconv" - "time" -) - -func convertToFloat(i interface{}) (float64, error) { - switch v := i.(type) { - case float64: - return v, nil - case string: - return strconv.ParseFloat(v, 64) - case int: - return float64(v), nil - case uint: - return float64(v), nil - case int64: - return float64(v), nil - case uint64: - return float64(v), nil - case time.Duration: - return v.Seconds(), nil - default: - return 0, fmt.Errorf("can't convert %T to float", v) - } -} diff --git a/helpers/templates/humamize_duration_test.go b/helpers/templates/humamize_duration_test.go deleted file mode 100644 index 426a5977..00000000 --- a/helpers/templates/humamize_duration_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2024 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package templates - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestHumanizeDurationSecondsFloat64(t *testing.T) { - input := []float64{0, 1, 60, 3600, 86400, 86400 + 3600, -(86400*2 + 3600*3 + 60*4 + 5), 899.99} - expected := []string{"0s", "1s", "1m 0s", "1h 0m 0s", "1d 0h 0m 0s", "1d 1h 0m 0s", "-2d 3h 4m 5s", "14m 59s"} - - for index, value := range input { - result, err := HumanizeDuration(value) - require.NoError(t, err) - require.Equal(t, expected[index], result) - } -} - -func TestHumanizeDurationSubsecondAndFractionalSecondsFloat64(t *testing.T) { - input := []float64{.1, .0001, .12345, 60.1, 60.5, 1.2345, 12.345} - expected := []string{"100ms", "100us", "123.5ms", "1m 0s", "1m 0s", "1.234s", "12.35s"} - - for index, value := range input { - result, err := HumanizeDuration(value) - require.NoError(t, err) - require.Equal(t, expected[index], result) - } -} - -func TestHumanizeDurationErrorString(t *testing.T) { - _, err := HumanizeDuration("one") - require.Error(t, err) -} - -func TestHumanizeDurationSecondsString(t *testing.T) { - input := []string{"0", "1", "60", "3600", "86400"} - expected := []string{"0s", "1s", "1m 0s", "1h 0m 0s", "1d 0h 0m 0s"} - - for index, value := range input { - result, err := HumanizeDuration(value) - require.NoError(t, err) - require.Equal(t, expected[index], result) - } -} - -func TestHumanizeDurationSubsecondAndFractionalSecondsString(t *testing.T) { - input := []string{".1", ".0001", ".12345", "60.1", "60.5", "1.2345", "12.345"} - expected := []string{"100ms", "100us", "123.5ms", "1m 0s", "1m 0s", "1.234s", "12.35s"} - - for index, value := range input { - result, err := HumanizeDuration(value) - require.NoError(t, err) - require.Equal(t, expected[index], result) - } -} - -func TestHumanizeDurationSecondsInt(t *testing.T) { - input := []int{0, -1, 1, 1234567} - expected := []string{"0s", "-1s", "1s", "14d 6h 56m 7s"} - - for index, value := range input { - result, err := HumanizeDuration(value) - require.NoError(t, err) - require.Equal(t, expected[index], result) - } -} diff --git a/helpers/templates/humanize_duration.go b/helpers/templates/time.go similarity index 79% rename from helpers/templates/humanize_duration.go rename to helpers/templates/time.go index be53fac7..266c8c99 100644 --- a/helpers/templates/humanize_duration.go +++ b/helpers/templates/time.go @@ -16,8 +16,31 @@ package templates import ( "fmt" "math" + "strconv" + "time" ) +func convertToFloat(i interface{}) (float64, error) { + switch v := i.(type) { + case float64: + return v, nil + case string: + return strconv.ParseFloat(v, 64) + case int: + return float64(v), nil + case uint: + return float64(v), nil + case int64: + return float64(v), nil + case uint64: + return float64(v), nil + case time.Duration: + return v.Seconds(), nil + default: + return 0, fmt.Errorf("can't convert %T to float", v) + } +} + func HumanizeDuration(i interface{}) (string, error) { v, err := convertToFloat(i) if err != nil { diff --git a/helpers/templates/time_test.go b/helpers/templates/time_test.go new file mode 100644 index 00000000..8c59b21b --- /dev/null +++ b/helpers/templates/time_test.go @@ -0,0 +1,141 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package templates + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestHumanizeDurationSecondsFloat64(t *testing.T) { + tc := []struct { + name string + input float64 + expected string + }{ + {name: "zero", input: 0, expected: "0s"}, + {name: "one second", input: 1, expected: "1s"}, + {name: "one minute", input: 60, expected: "1m 0s"}, + {name: "one hour", input: 3600, expected: "1h 0m 0s"}, + {name: "one day", input: 86400, expected: "1d 0h 0m 0s"}, + {name: "one day and one hour", input: 86400 + 3600, expected: "1d 1h 0m 0s"}, + {name: "negative duration", input: -(86400*2 + 3600*3 + 60*4 + 5), expected: "-2d 3h 4m 5s"}, + {name: "using a float", input: 899.99, expected: "14m 59s"}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + result, err := HumanizeDuration(tt.input) + require.NoError(t, err) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestHumanizeDurationSubsecondAndFractionalSecondsFloat64(t *testing.T) { + tc := []struct { + name string + input float64 + expected string + }{ + {name: "millseconds", input: .1, expected: "100ms"}, + {name: "nanoseconds", input: .0001, expected: "100us"}, + {name: "milliseconds + nanoseconds", input: .12345, expected: "123.5ms"}, + {name: "minute + millisecond", input: 60.1, expected: "1m 0s"}, + {name: "minute + milliseconds", input: 60.5, expected: "1m 0s"}, + {name: "second + milliseconds", input: 1.2345, expected: "1.234s"}, + {name: "second + milliseconds rounded", input: 12.345, expected: "12.35s"}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + result, err := HumanizeDuration(tt.input) + require.NoError(t, err) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestHumanizeDurationErrorString(t *testing.T) { + _, err := HumanizeDuration("one") + require.Error(t, err) +} + +func TestHumanizeDurationSecondsString(t *testing.T) { + tc := []struct { + name string + input string + expected string + }{ + {name: "zero", input: "0", expected: "0s"}, + {name: "second", input: "1", expected: "1s"}, + {name: "minute", input: "60", expected: "1m 0s"}, + {name: "hour", input: "3600", expected: "1h 0m 0s"}, + {name: "day", input: "86400", expected: "1d 0h 0m 0s"}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + result, err := HumanizeDuration(tt.input) + require.NoError(t, err) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestHumanizeDurationSubsecondAndFractionalSecondsString(t *testing.T) { + tc := []struct { + name string + input string + expected string + }{ + {name: "millseconds", input: ".1", expected: "100ms"}, + {name: "nanoseconds", input: ".0001", expected: "100us"}, + {name: "milliseconds + nanoseconds", input: ".12345", expected: "123.5ms"}, + {name: "minute + millisecond", input: "60.1", expected: "1m 0s"}, + {name: "minute + milliseconds", input: "60.5", expected: "1m 0s"}, + {name: "second + milliseconds", input: "1.2345", expected: "1.234s"}, + {name: "second + milliseconds rounded", input: "12.345", expected: "12.35s"}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + result, err := HumanizeDuration(tt.input) + require.NoError(t, err) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestHumanizeDurationSecondsInt(t *testing.T) { + tc := []struct { + name string + input int + expected string + }{ + {name: "zero", input: 0, expected: "0s"}, + {name: "negative", input: -1, expected: "-1s"}, + {name: "second", input: 1, expected: "1s"}, + {name: "days", input: 1234567, expected: "14d 6h 56m 7s"}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + result, err := HumanizeDuration(tt.input) + require.NoError(t, err) + require.Equal(t, tt.expected, result) + }) + } +}