prometheus/promql/parser/prettier_test.go
Julien Pivotto d41e5a5582
Prettifier: Add spaces with non-callable keywords (#11005)
* Prettifier: Add spaces with non-callable keywords

I prefer to have a difference between, on one side: functions calls, end(), start(), and on the other side with, without, ignoring, by and group_rrigt, group_left.

The reasoning is that the former ones are not calls, while other are
functions. Additionally, it matches the examples in our documentation.

Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>

* Fix tests

Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>
2022-07-15 00:09:56 +02:00

671 lines
13 KiB
Go

// Copyright 2022 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 parser
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestAggregateExprPretty(t *testing.T) {
maxCharactersPerLine = 10
inputs := []struct {
in, out string
}{
{
in: `sum(foo)`,
out: `sum(foo)`,
},
{
in: `sum by() (task:errors:rate10s{job="s"})`,
out: `sum(
task:errors:rate10s{job="s"}
)`,
},
{
in: `sum without(job,foo) (task:errors:rate10s{job="s"})`,
out: `sum without (job, foo) (
task:errors:rate10s{job="s"}
)`,
},
{
in: `sum(task:errors:rate10s{job="s"}) without(job,foo)`,
out: `sum without (job, foo) (
task:errors:rate10s{job="s"}
)`,
},
{
in: `sum by(job,foo) (task:errors:rate10s{job="s"})`,
out: `sum by (job, foo) (
task:errors:rate10s{job="s"}
)`,
},
{
in: `sum (task:errors:rate10s{job="s"}) by(job,foo)`,
out: `sum by (job, foo) (
task:errors:rate10s{job="s"}
)`,
},
{
in: `topk(10, ask:errors:rate10s{job="s"})`,
out: `topk(
10,
ask:errors:rate10s{job="s"}
)`,
},
{
in: `sum by(job,foo) (sum by(job,foo) (task:errors:rate10s{job="s"}))`,
out: `sum by (job, foo) (
sum by (job, foo) (
task:errors:rate10s{job="s"}
)
)`,
},
{
in: `sum by(job,foo) (sum by(job,foo) (sum by(job,foo) (task:errors:rate10s{job="s"})))`,
out: `sum by (job, foo) (
sum by (job, foo) (
sum by (job, foo) (
task:errors:rate10s{job="s"}
)
)
)`,
},
{
in: `sum by(job,foo)
(sum by(job,foo) (task:errors:rate10s{job="s"}))`,
out: `sum by (job, foo) (
sum by (job, foo) (
task:errors:rate10s{job="s"}
)
)`,
},
{
in: `sum by(job,foo)
(sum(task:errors:rate10s{job="s"}) without(job,foo))`,
out: `sum by (job, foo) (
sum without (job, foo) (
task:errors:rate10s{job="s"}
)
)`,
},
{
in: `sum by(job,foo) # Comment 1.
(sum by(job,foo) ( # Comment 2.
task:errors:rate10s{job="s"}))`,
out: `sum by (job, foo) (
sum by (job, foo) (
task:errors:rate10s{job="s"}
)
)`,
},
}
for _, test := range inputs {
expr, err := ParseExpr(test.in)
require.NoError(t, err)
require.Equal(t, test.out, Prettify(expr))
}
}
func TestBinaryExprPretty(t *testing.T) {
maxCharactersPerLine = 10
inputs := []struct {
in, out string
}{
{
in: `a+b`,
out: `a + b`,
},
{
in: `a == bool 1`,
out: ` a
== bool
1`,
},
{
in: `a + ignoring(job) b`,
out: ` a
+ ignoring (job)
b`,
},
{
in: `foo_1 + foo_2`,
out: ` foo_1
+
foo_2`,
},
{
in: `foo_1 + foo_2 + foo_3`,
out: ` foo_1
+
foo_2
+
foo_3`,
},
{
in: `foo + baar + foo_3`,
out: ` foo + baar
+
foo_3`,
},
{
in: `foo_1 + foo_2 + foo_3 + foo_4`,
out: ` foo_1
+
foo_2
+
foo_3
+
foo_4`,
},
{
in: `foo_1 + ignoring(foo) foo_2 + ignoring(job) group_left foo_3 + on(instance) group_right foo_4`,
out: ` foo_1
+ ignoring (foo)
foo_2
+ ignoring (job) group_left ()
foo_3
+ on (instance) group_right ()
foo_4`,
},
}
for _, test := range inputs {
t.Run(test.in, func(t *testing.T) {
expr, err := ParseExpr(test.in)
require.NoError(t, err)
require.Equal(t, test.out, Prettify(expr))
})
}
}
func TestCallExprPretty(t *testing.T) {
maxCharactersPerLine = 10
inputs := []struct {
in, out string
}{
{
in: `rate(foo[1m])`,
out: `rate(
foo[1m]
)`,
},
{
in: `sum_over_time(foo[1m])`,
out: `sum_over_time(
foo[1m]
)`,
},
{
in: `rate(long_vector_selector[10m:1m] @ start() offset 1m)`,
out: `rate(
long_vector_selector[10m:1m] @ start() offset 1m
)`,
},
{
in: `histogram_quantile(0.9, rate(foo[1m]))`,
out: `histogram_quantile(
0.9,
rate(
foo[1m]
)
)`,
},
{
in: `max_over_time(rate(demo_api_request_duration_seconds_count[1m])[1m:] @ start() offset 1m)`,
out: `max_over_time(
rate(
demo_api_request_duration_seconds_count[1m]
)[1m:] @ start() offset 1m
)`,
},
{
in: `label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*")`,
out: `label_replace(
up{job="api-server",service="a:c"},
"foo",
"$1",
"service",
"(.*):.*"
)`,
},
{
in: `label_replace(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*")`,
out: `label_replace(
label_replace(
up{job="api-server",service="a:c"},
"foo",
"$1",
"service",
"(.*):.*"
),
"foo",
"$1",
"service",
"(.*):.*"
)`,
},
}
for _, test := range inputs {
expr, err := ParseExpr(test.in)
require.NoError(t, err)
fmt.Println("=>", expr.String())
require.Equal(t, test.out, Prettify(expr))
}
}
func TestParenExprPretty(t *testing.T) {
maxCharactersPerLine = 10
inputs := []struct {
in, out string
}{
{
in: `(foo)`,
out: `(foo)`,
},
{
in: `(_foo_long_)`,
out: `(
_foo_long_
)`,
},
{
in: `((foo_long))`,
out: `(
(foo_long)
)`,
},
{
in: `((_foo_long_))`,
out: `(
(
_foo_long_
)
)`,
},
{
in: `(((foo_long)))`,
out: `(
(
(foo_long)
)
)`,
},
}
for _, test := range inputs {
expr, err := ParseExpr(test.in)
require.NoError(t, err)
require.Equal(t, test.out, Prettify(expr))
}
}
func TestStepInvariantExpr(t *testing.T) {
maxCharactersPerLine = 10
inputs := []struct {
in, out string
}{
{
in: `a @ 1`,
out: `a @ 1.000`,
},
{
in: `a @ start()`,
out: `a @ start()`,
},
{
in: `vector_selector @ start()`,
out: `vector_selector @ start()`,
},
}
for _, test := range inputs {
expr, err := ParseExpr(test.in)
require.NoError(t, err)
require.Equal(t, test.out, Prettify(expr))
}
}
func TestExprPretty(t *testing.T) {
maxCharactersPerLine = 10
inputs := []struct {
in, out string
}{
{
in: `(1 + 2)`,
out: `(1 + 2)`,
},
{
in: `(foo + bar)`,
out: `(
foo + bar
)`,
},
{
in: `(foo_long + bar_long)`,
out: `(
foo_long
+
bar_long
)`,
},
{
in: `(foo_long + bar_long + bar_2_long)`,
out: `(
foo_long
+
bar_long
+
bar_2_long
)`,
},
{
in: `((foo_long + bar_long) + bar_2_long)`,
out: `(
(
foo_long
+
bar_long
)
+
bar_2_long
)`,
},
{
in: `(1111 + 2222)`,
out: `(
1111
+
2222
)`,
},
{
in: `(sum_over_time(foo[1m]))`,
out: `(
sum_over_time(
foo[1m]
)
)`,
},
{
in: `histogram_quantile(0.9, rate(foo[1m] @ start()))`,
out: `histogram_quantile(
0.9,
rate(
foo[1m] @ start()
)
)`,
},
{
in: `(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"))`,
out: `(
label_replace(
up{job="api-server",service="a:c"},
"foo",
"$1",
"service",
"(.*):.*"
)
)`,
},
{
in: `(label_replace(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*"))`,
out: `(
label_replace(
label_replace(
up{job="api-server",service="a:c"},
"foo",
"$1",
"service",
"(.*):.*"
),
"foo",
"$1",
"service",
"(.*):.*"
)
)`,
},
{
in: `(label_replace(label_replace((up{job="api-server",service="a:c"}), "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*"))`,
out: `(
label_replace(
label_replace(
(
up{job="api-server",service="a:c"}
),
"foo",
"$1",
"service",
"(.*):.*"
),
"foo",
"$1",
"service",
"(.*):.*"
)
)`,
},
// Following queries have been taken from https://monitoring.mixins.dev/
{
in: `(node_filesystem_avail_bytes{job="node",fstype!=""} / node_filesystem_size_bytes{job="node",fstype!=""} * 100 < 40 and predict_linear(node_filesystem_avail_bytes{job="node",fstype!=""}[6h], 24*60*60) < 0 and node_filesystem_readonly{job="node",fstype!=""} == 0)`,
out: `(
node_filesystem_avail_bytes{fstype!="",job="node"}
/
node_filesystem_size_bytes{fstype!="",job="node"}
*
100
<
40
and
predict_linear(
node_filesystem_avail_bytes{fstype!="",job="node"}[6h],
24 * 60
*
60
)
<
0
and
node_filesystem_readonly{fstype!="",job="node"}
==
0
)`,
},
{
in: `(node_filesystem_avail_bytes{job="node",fstype!=""} / node_filesystem_size_bytes{job="node",fstype!=""} * 100 < 20 and predict_linear(node_filesystem_avail_bytes{job="node",fstype!=""}[6h], 4*60*60) < 0 and node_filesystem_readonly{job="node",fstype!=""} == 0)`,
out: `(
node_filesystem_avail_bytes{fstype!="",job="node"}
/
node_filesystem_size_bytes{fstype!="",job="node"}
*
100
<
20
and
predict_linear(
node_filesystem_avail_bytes{fstype!="",job="node"}[6h],
4 * 60
*
60
)
<
0
and
node_filesystem_readonly{fstype!="",job="node"}
==
0
)`,
},
{
in: `(node_timex_offset_seconds > 0.05 and deriv(node_timex_offset_seconds[5m]) >= 0) or (node_timex_offset_seconds < -0.05 and deriv(node_timex_offset_seconds[5m]) <= 0)`,
out: ` (
node_timex_offset_seconds
>
0.05
and
deriv(
node_timex_offset_seconds[5m]
)
>=
0
)
or
(
node_timex_offset_seconds
<
-0.05
and
deriv(
node_timex_offset_seconds[5m]
)
<=
0
)`,
},
{
in: `1 - ((node_memory_MemAvailable_bytes{job="node"} or (node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"}) ) / node_memory_MemTotal_bytes{job="node"})`,
out: ` 1
-
(
(
node_memory_MemAvailable_bytes{job="node"}
or
(
node_memory_Buffers_bytes{job="node"}
+
node_memory_Cached_bytes{job="node"}
+
node_memory_MemFree_bytes{job="node"}
+
node_memory_Slab_bytes{job="node"}
)
)
/
node_memory_MemTotal_bytes{job="node"}
)`,
},
{
in: `min by (job, integration) (rate(alertmanager_notifications_failed_total{job="alertmanager", integration=~".*"}[5m]) / rate(alertmanager_notifications_total{job="alertmanager", integration="~.*"}[5m])) > 0.01`,
out: ` min by (job, integration) (
rate(
alertmanager_notifications_failed_total{integration=~".*",job="alertmanager"}[5m]
)
/
rate(
alertmanager_notifications_total{integration="~.*",job="alertmanager"}[5m]
)
)
>
0.01`,
},
{
in: `(count by (job) (changes(process_start_time_seconds{job="alertmanager"}[10m]) > 4) / count by (job) (up{job="alertmanager"})) >= 0.5`,
out: ` (
count by (job) (
changes(
process_start_time_seconds{job="alertmanager"}[10m]
)
>
4
)
/
count by (job) (
up{job="alertmanager"}
)
)
>=
0.5`,
},
}
for _, test := range inputs {
expr, err := ParseExpr(test.in)
require.NoError(t, err)
require.Equal(t, test.out, Prettify(expr))
}
}
func TestUnaryPretty(t *testing.T) {
maxCharactersPerLine = 10
inputs := []struct {
in, out string
}{
{
in: `-1`,
out: `-1`,
},
{
in: `-vector_selector`,
out: `-vector_selector`,
},
{
in: `(-vector_selector)`,
out: `(
-vector_selector
)`,
},
{
in: `-histogram_quantile(0.9,rate(foo[1m]))`,
out: `-histogram_quantile(
0.9,
rate(
foo[1m]
)
)`,
},
{
in: `-histogram_quantile(0.99, sum by (le) (rate(foo[1m])))`,
out: `-histogram_quantile(
0.99,
sum by (le) (
rate(
foo[1m]
)
)
)`,
},
{
in: `-histogram_quantile(0.9, -rate(foo[1m] @ start()))`,
out: `-histogram_quantile(
0.9,
-rate(
foo[1m] @ start()
)
)`,
},
{
in: `(-histogram_quantile(0.9, -rate(foo[1m] @ start())))`,
out: `(
-histogram_quantile(
0.9,
-rate(
foo[1m] @ start()
)
)
)`,
},
}
for _, test := range inputs {
t.Run(test.in, func(t *testing.T) {
expr, err := ParseExpr(test.in)
require.NoError(t, err)
require.Equal(t, test.out, Prettify(expr))
})
}
}