blob: f3fffd237153fb0835908bbdbc929e758014d236 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"fmt"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
var cases = []struct {
input, output string
}{
{input: "", output: "\n"},
{input: " ", output: "\n"},
{input: "\n", output: "\n"},
{input: " \r\n\t", output: "\n"},
{input: " foo ", output: "foo\n"},
{input: "foo=0x42", output: "foo = 0x42\n"},
{input: "foo { }", output: "foo{}\n"},
{input: "( \r\n\t)", output: "()\n"},
{input: "[ \r\n\t]", output: "[]\n"},
{input: "{ \r\n\t}", output: "{}\n"},
{input: "(1,2,3,)", output: "(1, 2, 3)\n"},
{input: "[1,2,3,]", output: "[1, 2, 3]\n"},
{input: "{1,2,3,}", output: "{1, 2, 3}\n"},
{input: "{1,2,3,}", output: "{1, 2, 3}\n"},
{input: "{\n1\n}", output: "{\n 1,\n}\n"},
{input: "{\n1,\n2\n}", output: "{\n 1,\n 2,\n}\n"},
{input: "{\nfoo={} \n}", output: "{\n foo = {},\n}\n"},
{input: "foo \n\n\n bar \n\n\n baz \n\n\n", output: "foo\n\nbar\n\nbaz\n"},
{input: "foo : - 1", output: "foo: -1\n"},
{input: "foo : - 1.23e45", output: "foo: -1.23e45\n"},
{input: "foo( rights:A+B )", output: "foo(rights: A + B)\n"},
{input: "foo( rights:A-B )", output: "foo(rights: A - B)\n"},
{input: "// comment", output: "// comment\n"},
{input: " // comment", output: "// comment\n"},
{input: "foo// comment", output: "foo // comment\n"},
{input: "foo // comment", output: "foo // comment\n"},
{
input: `
// multiple
// line
// comment
// separate comment
// over here
`,
output: `
// multiple
// line
// comment
// separate comment
// over here
`,
},
{
input: `
// some comment
success ( "foo" ){
incorrect = "indentation)]}" ,
and = {
some, // another comment
more
}
}
`,
output: `
// some comment
success("foo") {
incorrect = "indentation)]}",
and = {
some, // another comment
more,
},
}
`,
},
{
input: `
decode_failure ( "TooManyHandles" ) {
// TODO: Close handles on encode/decode failure.
bindings_denylist=[ go,rust ,],
handle_defs ={
# 0=event( ),
# 1 = event ( ) ,
},
type=SingleHandle,
bytes ={
v1 ,v2= [
repeat( 0xff): 4 , padding : 4,
]
},
handles = {
v1 , v2 = [
#0,#1,
]
},
err = EXTRA_HANDLES,
}
`,
output: `
decode_failure("TooManyHandles") {
// TODO: Close handles on encode/decode failure.
bindings_denylist = [go, rust],
handle_defs = {
#0 = event(),
#1 = event(),
},
type = SingleHandle,
bytes = {
v1, v2 = [
repeat(0xff):4, padding:4,
],
},
handles = {
v1, v2 = [
#0, #1,
],
},
err = EXTRA_HANDLES,
}
`,
},
{
input: `
decode_failure ( "TooManyHandles" ) {
// TODO: Close handles on encode/decode failure.
bindings_denylist=[ go,rust ,],
handle_defs ={
# 0=event( ),
# 1 = event ( ) ,
},
// gidl-format off
type=SingleHandle,
bytes ={
v1 ,v2= [
repeat( 0xff): 4 , padding : 4,
// gidl-format on
]
},
}
`,
output: `
decode_failure("TooManyHandles") {
// TODO: Close handles on encode/decode failure.
bindings_denylist = [go, rust],
handle_defs = {
#0 = event(),
#1 = event(),
},
// gidl-format off
type=SingleHandle,
bytes ={
v1 ,v2= [
repeat( 0xff): 4 , padding : 4,
// gidl-format on
],
},
}
`,
},
{
input: `
encode_success("EncodeHandleTypeAndRightsComeFromFIDL") {
bindings_allowlist = [llcpp, hlcpp, go, rust, dart,],
handle_defs = {
# 0 = channel ( rights : channel_default ) ,
},
value = EventWithDefaultRights {h: #0},
bytes = {
v1, v2 = [
repeat( 0xff) :4, padding: 4
],
},
handle_dispositions = {
v1,v2 = [
{#0, type:event, rights:event_default }
]
}
}
`,
output: `
encode_success("EncodeHandleTypeAndRightsComeFromFIDL") {
bindings_allowlist = [llcpp, hlcpp, go, rust, dart],
handle_defs = {
#0 = channel(rights: channel_default),
},
value = EventWithDefaultRights{h: #0},
bytes = {
v1, v2 = [
repeat(0xff):4, padding:4,
],
},
handle_dispositions = {
v1, v2 = [
{#0, type: event, rights: event_default},
],
},
}
`,
},
}
func TestFormat(t *testing.T) {
for _, tc := range cases {
expectFormat(t, tc.input, tc.output)
}
}
func TestFormatIdempotent(t *testing.T) {
for _, tc := range cases {
expectFormat(t, tc.output, tc.output)
}
}
func TestFormatDisabled(t *testing.T) {
for _, tc := range cases {
// These directives don't nest.
if strings.Contains(tc.input, enableComment) {
continue
}
input := fmt.Sprintf("%s\n%s", disableComment, tc.input)
expectFormat(t, input, input)
}
}
func expectFormat(t *testing.T, input, output string) {
t.Helper()
var b strings.Builder
if err := format(&b, strings.NewReader(input), "<input>"); err != nil {
t.Errorf("unexpected error: %s\n\nfor input:\n%s", err, input)
} else if diff := cmp.Diff(output, b.String()); diff != "" {
t.Errorf("unexpected output (-want +got):\n%s\n\nfor input:\n%s", diff, input)
}
}
func TestFormatFails(t *testing.T) {
cases := []struct {
input, errSubstring string
}{
// Errors from text/scanner (without the specific message since it might
// change in later versions of Go):
{`"`, "<input>:1:1:"}, // unterminated string
{"'foo'", "<input>:1:1:"}, // invalid character literal
{"\xc3\x28", "<input>"}, // invalid UTF-8 (doesn't give a position)
// Errors from this package:
{")", "<input>:1:1: extraenous closing bracket ')'"},
{"]", "<input>:1:1: extraenous closing bracket ']'"},
{"}", "<input>:1:1: extraenous closing bracket '}'"},
{"(]", "<input>:1:2: mismatched closing bracket ']' (expected ')')"},
}
for _, tc := range cases {
var b strings.Builder
if err := format(&b, strings.NewReader(tc.input), "<input>"); err == nil {
t.Errorf("unexpected success for input:\n%s", tc.input)
} else if !strings.Contains(err.Error(), tc.errSubstring) {
t.Errorf("error %q did not contain %q, for input:\n%s", err, tc.errSubstring, tc.input)
}
}
}