blob: 6164b37b25517908574f1db88eaad9903bda7535 [file] [edit]
// Open source parser tests.
package parser
import (
"strings"
"testing"
"github.com/kylelemons/godebug/diff"
"github.com/kylelemons/godebug/pretty"
)
func TestError(t *testing.T) {
inputs := []struct {
in string
err string
}{{
in: "list_wrong_end: [one, two}",
}, {
in: "multy_string_list: ['a' 'b', 'c']",
err: "multiple-string value",
}, {
in: `child_list_notation: [
{`,
err: "[{}]",
}, {
in: `mapping {{
my_proto_field: "foo"
}}`, err: "Failed to find a FieldName",
}, {
in: `name: "string with literal new line
"`, err: "new line",
}}
for _, input := range inputs {
out, err := Format([]byte(input.in))
if err == nil {
nodes, err := Parse([]byte(input.in))
if err != nil {
t.Errorf("Parse %v returned err %v", input.in, err)
continue
}
t.Errorf("Expected a formatting error but none was raised while formatting:\n%v\nout\n%s\ntree\n%s\n", input.in, out, DebugFormat(nodes, 0))
continue
}
if !strings.Contains(err.Error(), input.err) {
t.Errorf(
"Expected a formatting error containing \"%v\",\n error was: \"%v\"\nwhile formatting:\n%v", input.err, err, input.in)
}
}
}
func TestPreprocess(t *testing.T) {
type testType int
const (
nonTripleQuotedTest testType = 1
tripleQuotedTest testType = 2
)
inputs := []struct {
name string
in string
want map[int]bool
err bool
testType testType
}{{
name: "simple example",
// 012
in: `p {}`,
want: map[int]bool{
2: true,
},
}, {
name: "multiple nested children in the same line",
// 0123456
in: `p { b { n: v } }`,
want: map[int]bool{
2: true,
6: true,
},
}, {
name: "only second line",
// 0123
in: `p {
b { n: v } }`,
want: map[int]bool{
6: true,
},
}, {
name: "empty output",
in: `p {
b {
n: v } }`,
want: map[int]bool{},
}, {
name: "comments and strings",
in: `
# p {}
s: "p {}"
# s: "p {}"
s: "#p {}"
p {}`,
want: map[int]bool{
// (5 lines) * (10 chars) - 2
58: true,
},
}, {
name: "escaped char",
in: `p { s="\"}"
}`,
want: map[int]bool{},
}, {
name: "missing '}'",
in: `p {`,
want: map[int]bool{},
}, {
name: "too many '}'",
in: `p {}}`,
err: true,
}, {
name: "single quote",
in: `"`,
err: true,
}, {
name: "double quote",
in: `""`,
}, {
name: "triple quoted backlash",
in: `"""\"""`,
err: false,
testType: tripleQuotedTest,
}, {
name: "triple quoted backlash invalid",
in: `"""\"""`,
err: true,
testType: nonTripleQuotedTest,
}, {
name: "triple quoted and regular quotes backslash handling",
in: `"""text""" "\""`,
err: false,
testType: tripleQuotedTest,
}}
for _, input := range inputs {
bytes := []byte(input.in)
// ensure capacity is equal to length to catch slice index out of bounds errors
bytes = bytes[0:len(bytes):len(bytes)]
if input.testType != tripleQuotedTest {
have, err := sameLineBrackets(bytes, false)
if (err != nil) != input.err {
t.Errorf("sameLineBrackets[%s] allowTripleQuotedStrings=false %v returned err %v", input.name, input.in, err)
continue
}
if diff := pretty.Compare(input.want, have); diff != "" {
t.Errorf("sameLineBrackets[%s] allowTripleQuotedStrings=false %v returned diff (-want, +have):\n%s", input.name, input.in, diff)
}
}
if input.testType != nonTripleQuotedTest {
have, err := sameLineBrackets(bytes, true)
if (err != nil) != input.err {
t.Errorf("sameLineBrackets[%s] allowTripleQuotedStrings=true %v returned err %v", input.name, input.in, err)
continue
}
if diff := pretty.Compare(input.want, have); diff != "" {
t.Errorf("sameLineBrackets[%s] allowTripleQuotedStrings=true %v returned diff (-want, +have):\n%s", input.name, input.in, diff)
}
}
}
}
func TestDisable(t *testing.T) {
inputs := []string{
`#txtpbfmt:disable
some{random:field}`,
`#a
#b
# txtpbfmt: disable
some{random:field}`,
}
for _, input := range inputs {
output, err := Format([]byte(input))
if err != nil {
t.Errorf("%v: %v", err, input)
continue
}
if diff := pretty.Compare(input, string(output)); diff != "" {
t.Errorf("disable ineffective (-want, +have):\n%s", diff)
}
}
}
func TestFormat(t *testing.T) {
inputs := []struct {
name string
in string
out string
}{{
name: "file comment + block comment",
in: `# file comment 1
# file comment 2
# presubmit comment 1
# presubmit comment 2
presubmit: {
# review comment 1
# review comment 2
review_notify: "address" # review same line comment 1
# extra comment block 1 line 1
# extra comment block 1 line 2
# extra comment block 2 line 1
# extra comment block 2 line 2
}
`,
out: `# file comment 1
# file comment 2
# presubmit comment 1
# presubmit comment 2
presubmit: {
# review comment 1
# review comment 2
review_notify: "address" # review same line comment 1
# extra comment block 1 line 1
# extra comment block 1 line 2
# extra comment block 2 line 1
# extra comment block 2 line 2
}
`}, {
name: "2x file comment",
in: `# file comment block 1 line 1
# file comment block 1 line 2
# file comment block 2 line 1
# file comment block 2 line 2
presubmit: {}
`,
out: `# file comment block 1 line 1
# file comment block 1 line 2
# file comment block 2 line 1
# file comment block 2 line 2
presubmit: {}
`}, {
name: "much blank space",
in: `
# presubmit comment 1
# presubmit comment 2
presubmit : {
# review comment 1
# review comment 2
review_notify : "address" # review same line comment 2
}
`,
out: `# presubmit comment 1
# presubmit comment 2
presubmit: {
# review comment 1
# review comment 2
review_notify: "address" # review same line comment 2
}
`}, {
name: "empty children",
in: `
# file comment 1
# file comment 2
# presubmit comment 1
# presubmit comment 2
presubmit: {
}
`,
out: `# file comment 1
# file comment 2
# presubmit comment 1
# presubmit comment 2
presubmit: {
}
`}, {
name: "list notation with []",
in: `
presubmit: {
check_tests: {
action: [ MAIL, REVIEW, SUBMIT ]
}
}
`,
out: `presubmit: {
check_tests: {
action: [MAIL, REVIEW, SUBMIT]
}
}
`}, {
name: "multiline string",
in: `
name: "Foo"
description:
"Foo is an open-source, scalable, and efficient storage solution "
"for the web. It is based on MySQL—so it supports major MySQL features "
"like transactions, indexes, and joins—but it also provides the scalability "
"of NoSQL. As such, Foo offers the best of both the RDBMS and NoSQL "
"worlds."
`,
out: `name: "Foo"
description:
"Foo is an open-source, scalable, and efficient storage solution "
"for the web. It is based on MySQL—so it supports major MySQL features "
"like transactions, indexes, and joins—but it also provides the scalability "
"of NoSQL. As such, Foo offers the best of both the RDBMS and NoSQL "
"worlds."
`}, {
name: "escaped \"",
in: `
check_contents: {
required_regexp: "\\s*syntax\\s*=\\s*\".*\""
}
`,
out: `check_contents: {
required_regexp: "\\s*syntax\\s*=\\s*\".*\""
}
`}, {
name: "single-quote inside a double-quote-delimited string (and vice-versa)",
in: `
description:
"foo's fork of mod_python's Cookie submodule. "
"as well as \"marshalled\" cookies -- cookies that contain marshalled python "
'double quote " inside single-quote-delimited string'
`,
out: `description:
"foo's fork of mod_python's Cookie submodule. "
"as well as \"marshalled\" cookies -- cookies that contain marshalled python "
"double quote \" inside single-quote-delimited string"
`}, {
name: "list with inline comments; comments after list and after last child",
in: `
tricorder: {
options: {
build_args: [
# Other build_args comment.
# LT.IfChange
"first line",
# LT.ThenChange(//foo)
"--config=android_x86", # Inline comment for android_x86.
"--config=android_release" # Inline comment for last child.
# Comment after list.
]
}
}
`,
out: `tricorder: {
options: {
build_args: [
# Other build_args comment.
# LT.IfChange
"first line",
# LT.ThenChange(//foo)
"--config=android_x86", # Inline comment for android_x86.
"--config=android_release" # Inline comment for last child.
# Comment after list.
]
}
}
`}, {
name: "';' at end of value",
in: `name: "value";`,
out: `name: "value"
`}, {
name: "multi-line string with inline comments",
in: `# cm
options:
"first line" # first comment
"second line" # second comment
`,
out: `# cm
options:
"first line" # first comment
"second line" # second comment
`}, {
name: "all kinds of inline comments",
in: `# presubmit pre comment 1
# presubmit pre comment 2
presubmit: {
# review pre comment 1
# review pre comment 2
review_notify: "review_notify_value" # review inline comment
# comment for project
project: [
# project1 pre comment 1
# project1 pre comment 2
"project1", # project1 inline comment
# project2 pre comment 1
# project2 pre comment 2
"project2" # project2 inline comment
# after comment 1
# after comment 2
]
# description pre comment 1
# description pre comment 2
description: "line1" # line1 inline comment
# line2 pre comment 1
# line2 pre comment 2
"line2" # line2 inline comment
# after comment 1
# after comment 2
name { name: value } # inline comment
} # inline comment
`,
out: `# presubmit pre comment 1
# presubmit pre comment 2
presubmit: {
# review pre comment 1
# review pre comment 2
review_notify: "review_notify_value" # review inline comment
# comment for project
project: [
# project1 pre comment 1
# project1 pre comment 2
"project1", # project1 inline comment
# project2 pre comment 1
# project2 pre comment 2
"project2" # project2 inline comment
# after comment 1
# after comment 2
]
# description pre comment 1
# description pre comment 2
description:
"line1" # line1 inline comment
# line2 pre comment 1
# line2 pre comment 2
"line2" # line2 inline comment
# after comment 1
# after comment 2
name { name: value } # inline comment
} # inline comment
`}, {
name: "more ';'",
in: `list_with_semicolon: [one, two];
string_with_semicolon: "str one";
multi_line_with_semicolon: "line 1"
"line 2";
other_name: other_value`,
out: `list_with_semicolon: [one, two]
string_with_semicolon: "str one"
multi_line_with_semicolon:
"line 1"
"line 2"
other_name: other_value
`}, {
name: "keep lists",
in: `list_two_items_inline: [one, two];
list_two_items: [one,
two];
list_one_item: [one]
list_one_item_multiline: [
one]
list_one_item_inline_comment: [one # with inline comment
]
list_one_item_pre_comment: [
# one item comment
one
]
list_one_item_post_comment: [
one
# post comment
]
list_no_item: []
list_no_item: [
]
# comment
list_no_item_comment: [
# as you can see there are no items
]
list_no_item_inline_comment: [] # Nothing here`,
out: `list_two_items_inline: [one, two]
list_two_items: [
one,
two
]
list_one_item: [one]
list_one_item_multiline: [
one
]
list_one_item_inline_comment: [
one # with inline comment
]
list_one_item_pre_comment: [
# one item comment
one
]
list_one_item_post_comment: [
one
# post comment
]
list_no_item: []
list_no_item: [
]
# comment
list_no_item_comment: [
# as you can see there are no items
]
list_no_item_inline_comment: [] # Nothing here
`}, {
name: "',' as field separator",
in: `# cm
presubmit: {
path_expression: "...",
other: "other"
}`,
out: `# cm
presubmit: {
path_expression: "..."
other: "other"
}
`}, {
name: "comment between name and value",
in: `
address: "address"
options:
# LT.IfChange
"first line"
# LT.ThenChange(//foo)
other: OTHER
`,
out: `address: "address"
options:
# LT.IfChange
"first line"
# LT.ThenChange(//foo)
other: OTHER
`}, {
name: "another example of comment between name and value",
in: `
address: # comment
"value"
options: # comment
"line 1"
"line 2"
`,
out: `address:
# comment
"value"
options:
# comment
"line 1"
"line 2"
`}, {
name: "new line with spaces between comment and value",
in: `
# comment
check_tests: {
}
`,
out: `# comment
check_tests: {
}
`}, {
name: "proto extension",
in: `[foo.bar.Baz] {
}`,
out: `[foo.bar.Baz] {
}
`}, {
name: "multiple nested in the same line",
in: `
expr {
union { userset { ref { relation: "_this" } } }
}
`,
out: `expr {
union { userset { ref { relation: "_this" } } }
}
`}, {
name: "comment on the last line without new line at the end of the file",
in: `name: "value" # comment`,
out: `name: "value" # comment
`}, {
name: "white space inside extension name",
in: `[foo.Bar.
Baz] {
name: "value"
}
`,
out: `[foo.Bar.Baz] {
name: "value"
}
`}, {
name: "one blank line at the end",
in: `presubmit {
}
`,
out: `presubmit {
}
`}, {
name: "template directive",
in: `[ext]: {
offset: %offset%
%if (offset < 0)% %for i : offset_count%
# directive comment
%if enabled%
# innermost comment
# innermost comment
offset_type: PACKETS
%end%
%end% %end%
# my comment
# my comment
%for (leading_timestamps : leading_timestamps_array)%
leading_timestamps: %leading_timestamps.timestamp%
%end%
}
`,
out: `[ext]: {
offset: %offset%
%if (offset < 0)%
%for i : offset_count%
# directive comment
%if enabled%
# innermost comment
# innermost comment
offset_type: PACKETS
%end%
%end%
%end%
# my comment
# my comment
%for (leading_timestamps : leading_timestamps_array)%
leading_timestamps: %leading_timestamps.timestamp%
%end%
}
`}, {
name: "template directive escaped",
in: `node {
name: %"value \"% value"%
}
`,
out: `node {
name: %"value \"% value"%
}
`}, {
name: "no_directives (as opposed to next tests)",
in: `presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
out: `presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`}, {
name: "expand_all_children",
in: `# txtpbfmt: expand_all_children
presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
out: `# txtpbfmt: expand_all_children
presubmit: {
check_presubmit_service: {
address: "address"
failure_status: WARNING
options: "options"
}
}
`}, {
name: "skip_all_colons",
in: `# txtpbfmt: skip_all_colons
presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
out: `# txtpbfmt: skip_all_colons
presubmit { check_presubmit_service { address: "address" failure_status: WARNING options: "options" } }
`}, {
name: "separate_directives",
in: `# txtpbfmt: expand_all_children
# txtpbfmt: skip_all_colons
presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
out: `# txtpbfmt: expand_all_children
# txtpbfmt: skip_all_colons
presubmit {
check_presubmit_service {
address: "address"
failure_status: WARNING
options: "options"
}
}
`}, {
name: "combined_directives",
in: `# txtpbfmt: expand_all_children, skip_all_colons
presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
out: `# txtpbfmt: expand_all_children, skip_all_colons
presubmit {
check_presubmit_service {
address: "address"
failure_status: WARNING
options: "options"
}
}
`}, {
name: "repeated proto format",
in: `{
a: 1;
b: 2}
{ a: 1;
b: 4
nested: {
# nested_string is optional
nested_string: "foo"
}
}
{ a: 1;
b:4,
c: 5}`,
out: `{
a: 1
b: 2
}
{
a: 1
b: 4
nested: {
# nested_string is optional
nested_string: "foo"
}
}
{
a: 1
b: 4
c: 5
}
`}, {
name: "repeated proto format with short messages",
in: `{ a: 1}
{ a: 2 }
{ a: 3}`,
out: `{ a: 1 }
{ a: 2 }
{ a: 3 }
`}, {
name: "allow unnamed nodes everywhere",
in: `
# txtpbfmt: allow_unnamed_nodes_everywhere
mapping {{
my_proto_field: "foo"
}}`,
out: `# txtpbfmt: allow_unnamed_nodes_everywhere
mapping {
{
my_proto_field: "foo"
}
}
`}, {
name: "sort fields and values",
in: `# txtpbfmt: sort_fields_by_field_name
# txtpbfmt: sort_repeated_fields_by_content
presubmit: {
auto_reviewers: "reviewerB"
check_contents: {
# Should go after ADD
operation: EDIT
operation: ADD
prohibited_regexp: "UnsafeFunction"
check_delta_only: true
}
# Should go before reviewerB
auto_reviewers: "reviewerA"
}
`,
out: `# txtpbfmt: sort_fields_by_field_name
# txtpbfmt: sort_repeated_fields_by_content
presubmit: {
# Should go before reviewerB
auto_reviewers: "reviewerA"
auto_reviewers: "reviewerB"
check_contents: {
check_delta_only: true
operation: ADD
# Should go after ADD
operation: EDIT
prohibited_regexp: "UnsafeFunction"
}
}
`}, {
name: "sort and remove duplicates",
in: `# txtpbfmt: sort_fields_by_field_name
# txtpbfmt: sort_repeated_fields_by_content
# txtpbfmt: remove_duplicate_values_for_repeated_fields
presubmit: {
auto_reviewers: "reviewerB"
# Should go before reviewerB
auto_reviewers: "reviewerA"
check_contents: {
operation: EDIT
operation: ADD
# Should be removed
operation: EDIT
prohibited_regexp: "UnsafeFunction"
# Should go before operation: ADD
check_delta_only: true
}
# Should be removed
auto_reviewers: "reviewerA"
}
`,
out: `# txtpbfmt: sort_fields_by_field_name
# txtpbfmt: sort_repeated_fields_by_content
# txtpbfmt: remove_duplicate_values_for_repeated_fields
presubmit: {
# Should go before reviewerB
auto_reviewers: "reviewerA"
auto_reviewers: "reviewerB"
check_contents: {
# Should go before operation: ADD
check_delta_only: true
operation: ADD
operation: EDIT
prohibited_regexp: "UnsafeFunction"
}
}
`}, {
name: "trailing comma / semicolon",
in: `dict: {
arg: {
key: "first_value"
value: { num: 0 }
},
arg: {
key: "second_value"
value: { num: 1 }
};
}
`,
out: `dict: {
arg: {
key: "first_value"
value: { num: 0 }
}
arg: {
key: "second_value"
value: { num: 1 }
}
}
`}, {
name: "LineLimit in all kinds of comments",
in: `# txtpbfmt: enable_line_limit
# presubmit pre comment with a very long line that should exced the 80 character limit 1
# presubmit pre comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
presubmit: {
# short review pre comment 1
# review pre comment with a very long line that should exced the 80 character limit 1
# review pre comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
# short review pre comment 2
# presubmit pre comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
review_notify: "review_notify_value" # review inline comment
# short comment for project
# comment for project with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
project: [
# short project1 pre comment 1
# project1 pre comment with a very long line that should exced the 80 character limit 1
# short project1 pre comment 2
# project1 pre comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
"project1", # project1 inline comment
# short project2 pre comment 1
# project2 pre comment with a very long line that should exced the 80 character limit 1
# short project2 pre comment 2
# project2 pre comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
"project2" # project2 inline comment
# after comment with a very long line that should exced the 80 character limit 1
# short after comment 1
# after comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
# short after comment 2
]
# short description pre comment 1
# description pre comment with a very long line that should exced the 80 character limit 1
# short description pre comment 2
# description pre comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
description:
"line1" # line1 inline comment
# short line2 pre comment 1
# line2 pre comment with a very long line that should exced the 80 character limit 1
# short line2 pre comment 2
# line2 pre comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
"line2" # line2 inline comment
# short after comment 1
# after comment with a very long line that should exced the 80 character limit 1
# short after comment 2
# after comment with a very long line that should exced the 80 character limit (this should be exceded by more than 160 character to force the creation of more than 2 lines) 2
name { name: value } # inline comment
} # inline comment
`,
out: `# txtpbfmt: enable_line_limit
# presubmit pre comment with a very long line that should exced the 80
# character limit 1
# presubmit pre comment with a very long line that should exced the 80
# character limit (this should be exceded by more than 160 character to force
# the creation of more than 2 lines) 2
presubmit: {
# short review pre comment 1
# review pre comment with a very long line that should exced the 80 character
# limit 1
# review pre comment with a very long line that should exced the 80 character
# limit (this should be exceded by more than 160 character to force the
# creation of more than 2 lines) 2
# short review pre comment 2
# presubmit pre comment with a very long line that should exced the 80
# character limit (this should be exceded by more than 160 character to force
# the creation of more than 2 lines) 2
review_notify: "review_notify_value" # review inline comment
# short comment for project
# comment for project with a very long line that should exced the 80 character
# limit (this should be exceded by more than 160 character to force the
# creation of more than 2 lines) 2
project: [
# short project1 pre comment 1
# project1 pre comment with a very long line that should exced the 80
# character limit 1
# short project1 pre comment 2
# project1 pre comment with a very long line that should exced the 80
# character limit (this should be exceded by more than 160 character to
# force the creation of more than 2 lines) 2
"project1", # project1 inline comment
# short project2 pre comment 1
# project2 pre comment with a very long line that should exced the 80
# character limit 1
# short project2 pre comment 2
# project2 pre comment with a very long line that should exced the 80
# character limit (this should be exceded by more than 160 character to
# force the creation of more than 2 lines) 2
"project2" # project2 inline comment
# after comment with a very long line that should exced the 80 character
# limit 1
# short after comment 1
# after comment with a very long line that should exced the 80 character
# limit (this should be exceded by more than 160 character to force the
# creation of more than 2 lines) 2
# short after comment 2
]
# short description pre comment 1
# description pre comment with a very long line that should exced the 80
# character limit 1
# short description pre comment 2
# description pre comment with a very long line that should exced the 80
# character limit (this should be exceded by more than 160 character to force
# the creation of more than 2 lines) 2
description:
"line1" # line1 inline comment
# short line2 pre comment 1
# line2 pre comment with a very long line that should exced the 80 character
# limit 1
# short line2 pre comment 2
# line2 pre comment with a very long line that should exced the 80 character
# limit (this should be exceded by more than 160 character to force the
# creation of more than 2 lines) 2
"line2" # line2 inline comment
# short after comment 1
# after comment with a very long line that should exced the 80 character limit
# 1
# short after comment 2
# after comment with a very long line that should exced the 80 character limit
# (this should be exceded by more than 160 character to force the creation of
# more than 2 lines) 2
name { name: value } # inline comment
} # inline comment
`},
}
for _, input := range inputs {
out, err := Format([]byte(input.in))
if err != nil {
t.Errorf("Format[%s] %v returned err %v", input.name, input.in, err)
continue
}
if diff := diff.Diff(input.out, string(out)); diff != "" {
nodes, err := Parse([]byte(input.in))
if err != nil {
t.Errorf("Parse[%s] %v returned err %v", input.name, input.in, err)
continue
}
t.Errorf("Format[%s](\n%s\n)\nparsed tree\n%s\n\nreturned diff (-want, +got):\n%s", input.name, input.in, DebugFormat(nodes, 0), diff)
}
}
}
func TestParserConfigs(t *testing.T) {
inputs := []struct {
name string
in string
config Config
out string
}{{
name: "AlreadyExpandedConfigOff",
in: `presubmit: {
check_presubmit_service: {
address: "address"
failure_status: WARNING
options: "options"
}
}
`,
config: Config{ExpandAllChildren: false},
out: `presubmit: {
check_presubmit_service: {
address: "address"
failure_status: WARNING
options: "options"
}
}
`,
}, {
name: "AlreadyExpandedConfigOn",
in: `presubmit: {
check_presubmit_service: {
address: "address"
failure_status: WARNING
options: "options"
}
}
`,
config: Config{ExpandAllChildren: true},
out: `presubmit: {
check_presubmit_service: {
address: "address"
failure_status: WARNING
options: "options"
}
}
`,
}, {
name: "NotExpandedOnFix",
in: `presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
config: Config{ExpandAllChildren: false},
out: `presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
}, {
name: "ExpandedOnFix",
in: `presubmit: { check_presubmit_service: { address: "address" failure_status: WARNING options: "options" } }
`,
config: Config{ExpandAllChildren: true},
out: `presubmit: {
check_presubmit_service: {
address: "address"
failure_status: WARNING
options: "options"
}
}
`,
}, {
name: "SortFieldNames",
in: `presubmit: {
auto_reviewers: "reviewerB"
check_contents: {
operation: EDIT
operation: ADD
prohibited_regexp: "UnsafeFunction"
check_delta_only: true
}
# Should remain below reviewerB
auto_reviewers: "reviewerA"
}
`,
config: Config{SortFieldsByFieldName: true},
out: `presubmit: {
auto_reviewers: "reviewerB"
# Should remain below reviewerB
auto_reviewers: "reviewerA"
check_contents: {
check_delta_only: true
operation: EDIT
operation: ADD
prohibited_regexp: "UnsafeFunction"
}
}
`,
}, {
name: "SortFieldContents",
in: `presubmit: {
auto_reviewers: "reviewerB"
check_contents: {
# Should go after ADD
operation: EDIT
operation: ADD
prohibited_regexp: "UnsafeFunction"
check_delta_only: true
}
# Should remain below
auto_reviewers: "reviewerA"
}
`,
config: Config{SortRepeatedFieldsByContent: true},
out: `presubmit: {
auto_reviewers: "reviewerB"
check_contents: {
operation: ADD
# Should go after ADD
operation: EDIT
prohibited_regexp: "UnsafeFunction"
check_delta_only: true
}
# Should remain below
auto_reviewers: "reviewerA"
}
`,
}, {
name: "SortFieldNamesAndContents",
in: `presubmit: {
auto_reviewers: "reviewerB"
check_contents: {
# Should go after ADD
operation: EDIT
operation: ADD
prohibited_regexp: "UnsafeFunction"
check_delta_only: true
}
# Should go before reviewerB
auto_reviewers: "reviewerA"
}
`,
config: Config{SortFieldsByFieldName: true, SortRepeatedFieldsByContent: true},
out: `presubmit: {
# Should go before reviewerB
auto_reviewers: "reviewerA"
auto_reviewers: "reviewerB"
check_contents: {
check_delta_only: true
operation: ADD
# Should go after ADD
operation: EDIT
prohibited_regexp: "UnsafeFunction"
}
}
`,
}, {
name: "RemoveRepeats",
in: `presubmit: {
auto_reviewers: "reviewerB"
auto_reviewers: "reviewerA"
check_contents: {
operation: EDIT
operation: ADD
# Should be removed
operation: EDIT
prohibited_regexp: "UnsafeFunction"
check_delta_only: true
}
# Should be removed
auto_reviewers: "reviewerA"
}
`,
config: Config{RemoveDuplicateValuesForRepeatedFields: true},
out: `presubmit: {
auto_reviewers: "reviewerB"
auto_reviewers: "reviewerA"
check_contents: {
operation: EDIT
operation: ADD
prohibited_regexp: "UnsafeFunction"
check_delta_only: true
}
}
`,
}, {
name: "SortEverythingAndRemoveRepeats",
in: `presubmit: {
auto_reviewers: "reviewerB"
# Should go before reviewerB
auto_reviewers: "reviewerA"
check_contents: {
operation: EDIT
operation: ADD
# Should be removed
operation: EDIT
prohibited_regexp: "UnsafeFunction"
# Should go before operation: ADD
check_delta_only: true
}
# Should be removed
auto_reviewers: "reviewerA"
}
`,
config: Config{
SortFieldsByFieldName: true,
SortRepeatedFieldsByContent: true,
RemoveDuplicateValuesForRepeatedFields: true},
out: `presubmit: {
# Should go before reviewerB
auto_reviewers: "reviewerA"
auto_reviewers: "reviewerB"
check_contents: {
# Should go before operation: ADD
check_delta_only: true
operation: ADD
operation: EDIT
prohibited_regexp: "UnsafeFunction"
}
}
`,
}, {
name: "TripleQuotedStrings",
config: Config{
AllowTripleQuotedStrings: true,
},
in: `foo: """bar"""
`,
out: `foo: """bar"""
`,
}, {
name: "TripleQuotedStrings_multiLine",
config: Config{
AllowTripleQuotedStrings: true,
},
in: `foo: """
bar
"""
`,
out: `foo: """
bar
"""
`,
}, {
name: "TripleQuotedStrings_singleQuotes",
config: Config{
AllowTripleQuotedStrings: true,
},
in: `foo: '''
bar
'''
`,
out: `foo: '''
bar
'''
`,
}, {
name: "TripleQuotedStrings_brackets",
config: Config{
AllowTripleQuotedStrings: true,
},
in: `s: """ "}" """
`,
out: `s: """ "}" """
`,
}, {
name: "TripleQuotedStrings_metaComment",
in: `# txtpbfmt: allow_triple_quoted_strings
foo: """
bar
"""
`,
out: `# txtpbfmt: allow_triple_quoted_strings
foo: """
bar
"""
`,
}}
// Test FormatWithConfig with inputs.
for _, input := range inputs {
got, err := FormatWithConfig([]byte(input.in), input.config)
if err != nil {
t.Errorf("FormatWithConfig[%s] %v with config %v returned err %v", input.name, input.in, input.config, err)
continue
}
if diff := diff.Diff(input.out, string(got)); diff != "" {
t.Errorf("FormatWithConfig[%s](\n%s\n)\nreturned different output from expected (-want, +got):\n%s", input.name, input.in, diff)
}
}
// Test ParseWithConfig with inputs.
for _, input := range inputs {
config := input.config
nodes, err := ParseWithConfig([]byte(input.in), &config)
if err != nil {
t.Errorf("ParseWithConfig[%s] %v with config %v returned err %v", input.name, input.in, input.config, err)
continue
}
got := Pretty(nodes, 0)
if diff := diff.Diff(input.out, got); diff != "" {
t.Errorf("ParseWithConfig[%s](\n%s\n)\nreturned different Pretty output from expected (-want, +got):\n%s", input.name, input.in, diff)
}
}
}
func TestDebugFormat(t *testing.T) {
inputs := []struct {
in string
want string
}{{
in: `name: { name: "value" }`,
want: `
name: "name"
PreComments: "" (len 0)
children:
. name: "name"
. PreComments: "" (len 0)
. values: [{Value: "\"value\"", PreComments: "", InlineComment: ""}]
`,
}}
for _, input := range inputs {
nodes, err := Parse([]byte(input.in))
if err != nil {
t.Errorf("Parse %v returned err %v", input.in, err)
continue
}
if len(nodes) == 0 {
t.Errorf("Parse %v returned no nodes", input.in)
continue
}
got := DebugFormat(nodes, 0 /* depth */)
if diff := diff.Diff(input.want, got); diff != "" {
t.Errorf("DebugFormat %v returned diff (-want, +got):\n%s", input.in, diff)
}
}
}