blob: 75bc0ae974360f152a7b4c68fdf51a8ed4c9d161 [file] [log] [blame]
// Copyright 2022 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 zither
import (
"fmt"
"sort"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgentest"
)
// Permits the comparison of types with unexported fields.
var cmpOpt = cmp.AllowUnexported(
fidlgen.LibraryName{},
fidlgen.Name{},
Const{},
Enum{},
EnumMember{},
Bits{},
BitsMember{},
Struct{},
StructMember{},
Overlay{},
OverlayVariant{},
Alias{},
SyscallFamily{},
Syscall{},
SyscallParameter{},
)
func TestGeneratedFileCount(t *testing.T) {
{
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
const A bool = true;
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
if len(summary.Files) != 1 {
t.Fatalf("expected one summary; got %d", len(summary.Files))
}
}
{
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Multiple([]string{
`
library example;
const A bool = true;
`,
`
library example;
const B bool = true;
`,
`
library example;
const C bool = true;
`,
})
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
if len(summary.Files) != 3 {
t.Fatalf("expected three summary.Files; got %d", len(summary.Files))
}
}
}
func TestCanSummarizeLibraryName(t *testing.T) {
name := "this.is.an.example.library"
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(fmt.Sprintf(`
library %s;
const A bool = true;
`, name))
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
if summary.Files[0].Library.String() != name {
t.Errorf("expected %s; got %s", name, summary.Files[0].Library)
}
}
func TestDeclOrder(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
const A int32 = 0;
const B int32 = E;
const C int32 = A;
const D int32 = 1;
const E int32 = C;
const F int32 = B;
const G int32 = 2;
`)
expectedDepOrder := []string{
"example/A",
"example/C",
"example/D", // C and D have no interdependencies, and D follows C in source.
"example/E",
"example/B",
"example/F",
"example/G",
}
{
var seenByCallback []string
summary, err := Summarize(ir, wd, SourceDeclOrder, func(decl Decl) {
seenByCallback = append(seenByCallback, decl.GetName().String())
})
if err != nil {
t.Fatal(err)
}
var actual []string
for _, decl := range summary.Files[0].Decls {
actual = append(actual, decl.Name().String())
}
expectedSourceOrder := []string{
"example/A",
"example/B",
"example/C",
"example/D",
"example/E",
"example/F",
"example/G",
}
if diff := cmp.Diff(expectedSourceOrder, actual); diff != "" {
t.Error(diff)
}
if diff := cmp.Diff(expectedDepOrder, seenByCallback); diff != "" {
t.Errorf(diff)
}
}
{
var seenByCallback []string
summary, err := Summarize(ir, wd, DependencyDeclOrder, func(decl Decl) {
seenByCallback = append(seenByCallback, decl.GetName().String())
})
if err != nil {
t.Fatal(err)
}
var actual []string
for _, decl := range summary.Files[0].Decls {
actual = append(actual, decl.Name().String())
}
if diff := cmp.Diff(expectedDepOrder, actual); diff != "" {
t.Error(diff)
}
if diff := cmp.Diff(expectedDepOrder, seenByCallback); diff != "" {
t.Errorf(diff)
}
}
}
func TestFloatConstantsAreDisallowed(t *testing.T) {
decls := []string{
"const FLOAT32 float32 = 0.0;",
"const FLOAT64 float64 = 0.0;",
}
for _, decl := range decls {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(fmt.Sprintf(`
library example;
%s
`, decl))
_, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err == nil {
t.Fatal("expected an error")
}
if err.Error() != "floats are unsupported" {
t.Errorf("unexpected error: %v", err)
}
}
}
func TestCanSummarizeConstants(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
const BOOL bool = false;
const BINARY_UINT8 uint8 = 0b10101111;
const HEX_UINT16 uint16 = 0xabcd;
const DECIMAL_UINT32 uint32 = 123456789;
const BINARY_INT8 int8 = 0b1111010;
const HEX_INT16 int16 = 0xcba;
const NEGATIVE_HEX_INT16 int16 = -0xcba;
const DECIMAL_INT32 int32 = 1050065;
const NEGATIVE_DECIMAL_INT32 int32 = -1050065;
const UINT64_MAX uint64 = 0xffffffffffffffff;
const INT64_MIN int64 = -0x8000000000000000;
const SOME_STRING string = "XXX";
const DEFINED_IN_TERMS_OF_ANOTHER_STRING string = SOME_STRING;
const DEFINED_IN_TERMS_OF_ANOTHER_UINT16 uint16 = HEX_UINT16;
type Uint8Enum = strict enum : uint8 {
MAX = 0xff;
};
const UINT8_ENUM_VALUE Uint8Enum = Uint8Enum.MAX;
/// This is a one-line comment.
const COMMENTED_BOOL bool = true;
/// This is
/// a
/// many-line
/// comment.
const COMMENTED_STRING string = "YYY";
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
var actual []Const
for _, decl := range summary.Files[0].Decls {
if decl.IsConst() {
actual = append(actual, decl.AsConst())
}
}
someString := Const{
decl: decl{Name: fidlgen.MustReadName("example/SOME_STRING")},
Kind: TypeKindString,
Type: "string",
Value: "XXX",
}
hexUint16 := Const{
decl: decl{Name: fidlgen.MustReadName("example/HEX_UINT16")},
Kind: TypeKindInteger,
Type: "uint16",
Value: "0xabcd",
}
uint8Enum := Enum{
decl: decl{Name: fidlgen.MustReadName("example/Uint8Enum")},
Subtype: "uint8",
Members: []EnumMember{
{
member: member{Name: "MAX"},
Value: "0xff",
},
},
}
// Listed in declaration order for readability, but similarly sorted.
expected := []Const{
{
decl: decl{Name: fidlgen.MustReadName("example/BOOL")},
Kind: TypeKindBool,
Type: "bool",
Value: "false",
},
{
decl: decl{Name: fidlgen.MustReadName("example/BINARY_UINT8")},
Kind: TypeKindInteger,
Type: "uint8",
Value: "0b10101111",
},
hexUint16,
{
decl: decl{Name: fidlgen.MustReadName("example/DECIMAL_UINT32")},
Kind: TypeKindInteger,
Type: "uint32",
Value: "123456789",
},
{
decl: decl{Name: fidlgen.MustReadName("example/BINARY_INT8")},
Kind: TypeKindInteger,
Type: "int8",
Value: "0b1111010",
},
{
decl: decl{Name: fidlgen.MustReadName("example/HEX_INT16")},
Kind: TypeKindInteger,
Type: "int16",
Value: "0xcba",
},
{
decl: decl{Name: fidlgen.MustReadName("example/NEGATIVE_HEX_INT16")},
Kind: TypeKindInteger,
Type: "int16",
Value: "-0xcba",
},
{
decl: decl{Name: fidlgen.MustReadName("example/DECIMAL_INT32")},
Kind: TypeKindInteger,
Type: "int32",
Value: "1050065",
},
{
decl: decl{Name: fidlgen.MustReadName("example/NEGATIVE_DECIMAL_INT32")},
Kind: TypeKindInteger,
Type: "int32",
Value: "-1050065",
},
{
decl: decl{Name: fidlgen.MustReadName("example/UINT64_MAX")},
Kind: TypeKindInteger,
Type: "uint64",
Value: "0xffffffffffffffff",
},
{
decl: decl{Name: fidlgen.MustReadName("example/INT64_MIN")},
Kind: TypeKindInteger,
Type: "int64",
Value: "-0x8000000000000000",
},
someString,
{
decl: decl{Name: fidlgen.MustReadName("example/DEFINED_IN_TERMS_OF_ANOTHER_STRING")},
Kind: TypeKindString,
Type: "string",
Value: "XXX",
Element: &ConstElementValue{Decl: &someString},
},
{
decl: decl{Name: fidlgen.MustReadName("example/DEFINED_IN_TERMS_OF_ANOTHER_UINT16")},
Kind: TypeKindInteger,
Type: "uint16",
Value: "43981",
Element: &ConstElementValue{Decl: &hexUint16},
},
{
decl: decl{Name: fidlgen.MustReadName("example/UINT8_ENUM_VALUE")},
Kind: TypeKindEnum,
Type: "example/Uint8Enum",
Value: "255",
Element: &ConstElementValue{
Decl: &uint8Enum,
Member: uint8Enum.Members[0],
},
},
{
decl: decl{
Name: fidlgen.MustReadName("example/COMMENTED_BOOL"),
Comments: []string{" This is a one-line comment."},
},
Kind: TypeKindBool,
Type: "bool",
Value: "true",
},
{
decl: decl{
Name: fidlgen.MustReadName("example/COMMENTED_STRING"),
Comments: []string{" This is", " a", " many-line", " comment."},
},
Kind: TypeKindString,
Type: "string",
Value: "YYY",
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
func TestCanSummarizeEnums(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
/// This is a uint8 enum.
type Uint8Enum = enum : uint8 {
/// This is a member.
TWO = 0b10;
/// This is
/// another
/// member.
SEVENTEEN = 17;
};
/// This
/// is
/// an
/// int64 enum.
type Int64Enum = enum : int64 {
MINUS_HEX_ABCD = -0xabcd;
ORED_VALUE = 0x10 | 0x01;
HEX_DEADBEEF = 0xdeadbeef;
};
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
// Normalize member order by name for a stable comparison.
var actual []Enum
for _, decl := range summary.Files[0].Decls {
if decl.IsEnum() {
enum := decl.AsEnum()
sort.Slice(enum.Members, func(i, j int) bool {
return strings.Compare(enum.Members[i].Name, enum.Members[j].Name) < 0
})
actual = append(actual, enum)
}
}
expected := []Enum{
{
decl: decl{
Name: fidlgen.MustReadName("example/Uint8Enum"),
Comments: []string{" This is a uint8 enum."},
},
Subtype: "uint8",
Members: []EnumMember{
{
member: member{
Name: "SEVENTEEN",
Comments: []string{" This is", " another", " member."},
},
Value: "17",
},
{
member: member{
Name: "TWO",
Comments: []string{" This is a member."},
},
Value: "0b10",
},
},
},
{
Subtype: "int64",
decl: decl{
Name: fidlgen.MustReadName("example/Int64Enum"),
Comments: []string{" This", " is", " an", " int64 enum."},
},
Members: []EnumMember{
{
member: member{Name: "HEX_DEADBEEF"},
Value: "0xdeadbeef",
},
{
member: member{Name: "MINUS_HEX_ABCD"},
Value: "-0xabcd",
},
{
member: member{Name: "ORED_VALUE"},
Value: "17",
Expression: "0x10 | 0x01",
},
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
func TestCanSummarizeBits(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
/// This is a uint8 bits.
type Uint8Bits = bits : uint8 {
/// This is a member.
ONE = 0b1;
/// This is
/// another
/// member.
SIXTEEN = 16;
};
/// This
/// is
/// a
/// uint64 bits.
type Uint64Bits = bits : uint64 {
MEMBER = 0x1000;
};
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
// Normalize member order by name for a stable comparison.
var actual []Bits
for _, decl := range summary.Files[0].Decls {
if decl.IsBits() {
bits := decl.AsBits()
sort.Slice(bits.Members, func(i, j int) bool {
return strings.Compare(bits.Members[i].Name, bits.Members[j].Name) < 0
})
actual = append(actual, bits)
}
}
expected := []Bits{
{
Subtype: fidlgen.Uint8,
decl: decl{
Name: fidlgen.MustReadName("example/Uint8Bits"),
Comments: []string{" This is a uint8 bits."},
},
Members: []BitsMember{
{
member: member{
Name: "ONE",
Comments: []string{" This is a member."},
},
Index: 0,
},
{
member: member{
Name: "SIXTEEN",
Comments: []string{" This is", " another", " member."},
},
Index: 4,
},
},
},
{
Subtype: fidlgen.Uint64,
decl: decl{
Name: fidlgen.MustReadName("example/Uint64Bits"),
Comments: []string{" This", " is", " a", " uint64 bits."},
},
Members: []BitsMember{
{
member: member{Name: "MEMBER"},
Index: 12,
},
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
func TestCanSummarizeStructs(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
/// This is a struct.
type EmptyStruct = struct {};
type BasicStruct = struct {
/// This is a struct member.
i64 int64;
u64 uint64;
i32 int32;
u32 uint32;
i16 int16;
u16 uint16;
i8 int8;
u8 uint8;
b bool;
e Enum;
bits Bits;
empty EmptyStruct;
};
type Enum = enum : uint16 {
ZERO = 0;
};
type Bits = bits : uint16 {
ONE = 1;
};
type StructWithArrayMembers = struct {
u8s array<uint8, 10>;
empties array<EmptyStruct, 6>;
nested array<array<bool, 2>, 4>;
};
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
var actual []Struct
for _, decl := range summary.Files[0].Decls {
if decl.IsStruct() {
actual = append(actual, decl.AsStruct())
}
}
// Addressable integers for use as TypeDescriptor.ElementCount below.
two, four, six, ten := 2, 4, 6, 10
emptyStruct := Struct{
decl: decl{
Name: fidlgen.MustReadName("example/EmptyStruct"),
Comments: []string{" This is a struct."},
},
Size: 1,
}
enum := Enum{
decl: decl{Name: fidlgen.MustReadName("example/Enum")},
Subtype: "uint16",
Members: []EnumMember{
{
member: member{Name: "ZERO"},
Value: "0",
},
},
}
bits := Bits{
decl: decl{Name: fidlgen.MustReadName("example/Bits")},
Subtype: "uint16",
Members: []BitsMember{
{
member: member{Name: "ONE"},
Index: 0,
},
},
}
expected := []Struct{
emptyStruct,
{
decl: decl{
Name: fidlgen.MustReadName("example/BasicStruct"),
},
Size: 40,
HasPadding: true,
Members: []StructMember{
{
member: member{
Name: "i64",
Comments: []string{" This is a struct member."},
},
Type: TypeDescriptor{
Type: "int64",
Kind: TypeKindInteger,
Size: 8,
},
Offset: 0,
},
{
member: member{Name: "u64"},
Type: TypeDescriptor{
Type: "uint64",
Kind: TypeKindInteger,
Size: 8,
},
Offset: 8,
},
{
member: member{Name: "i32"},
Type: TypeDescriptor{
Type: "int32",
Kind: TypeKindInteger,
Size: 4,
},
Offset: 16,
},
{
member: member{Name: "u32"},
Type: TypeDescriptor{
Type: "uint32",
Kind: TypeKindInteger,
Size: 4,
},
Offset: 20,
},
{
member: member{Name: "i16"},
Type: TypeDescriptor{
Type: "int16",
Kind: TypeKindInteger,
Size: 2,
},
Offset: 24,
},
{
member: member{Name: "u16"},
Type: TypeDescriptor{
Type: "uint16",
Kind: TypeKindInteger,
Size: 2,
},
Offset: 26,
},
{
member: member{Name: "i8"},
Type: TypeDescriptor{
Type: "int8",
Kind: TypeKindInteger,
Size: 1,
},
Offset: 28,
},
{
member: member{Name: "u8"},
Type: TypeDescriptor{
Type: "uint8",
Kind: TypeKindInteger,
Size: 1,
},
Offset: 29,
},
{
member: member{Name: "b"},
Type: TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
Offset: 30,
},
{
member: member{Name: "e"},
Type: TypeDescriptor{
Type: "example/Enum",
Kind: TypeKindEnum,
Decl: &enum,
Size: 2,
},
Offset: 32,
},
{
member: member{Name: "bits"},
Type: TypeDescriptor{
Type: "example/Bits",
Kind: TypeKindBits,
Decl: &bits,
Size: 2,
},
Offset: 34,
},
{
member: member{Name: "empty"},
Type: TypeDescriptor{
Type: "example/EmptyStruct",
Kind: TypeKindStruct,
Decl: &emptyStruct,
Size: 1,
},
Offset: 36,
},
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/StructWithArrayMembers")},
Size: 24,
Members: []StructMember{
{
member: member{Name: "u8s"},
Type: TypeDescriptor{
Kind: TypeKindArray,
ElementType: &TypeDescriptor{
Type: "uint8",
Kind: TypeKindInteger,
Size: 1,
},
ElementCount: &ten,
Size: 10,
},
Offset: 0,
},
{
member: member{Name: "empties"},
Type: TypeDescriptor{
Kind: TypeKindArray,
ElementType: &TypeDescriptor{
Type: "example/EmptyStruct",
Kind: TypeKindStruct,
Decl: &emptyStruct,
Size: 1,
},
ElementCount: &six,
Size: 6,
},
Offset: 10,
},
{
member: member{Name: "nested"},
Type: TypeDescriptor{
Kind: TypeKindArray,
ElementType: &TypeDescriptor{
Kind: TypeKindArray,
ElementType: &TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
ElementCount: &two,
Size: 2,
},
ElementCount: &four,
Size: 8,
},
Offset: 16,
},
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
func TestCanSummarizeOverlays(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).WithExperiment("zx_c_types").Single(`
library example;
/// This is an overlay.
type BasicOverlay = strict overlay{
/// This is an overlay variant.
1: i64 int64;
2: u64 uint64;
3: i32 int32;
4: u32 uint32;
5: i16 int16;
6: u16 uint16;
7: i8 int8;
8: u8 uint8;
9: b bool;
10: e Enum;
11: bits Bits;
12: s OverlayStructVariant;
};
type OverlayStructVariant = struct {
value uint64;
};
type Enum = enum : uint16 {
ZERO = 0;
};
type Bits = bits : uint16 {
ONE = 1;
};
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
var actual []Overlay
for _, decl := range summary.Files[0].Decls {
if decl.IsOverlay() {
actual = append(actual, decl.AsOverlay())
}
}
structVariant := Struct{
decl: decl{
Name: fidlgen.MustReadName("example/OverlayStructVariant"),
},
Size: 8,
Members: []StructMember{
{
member: member{Name: "value"},
Type: TypeDescriptor{
Type: "uint64",
Kind: TypeKindInteger,
Size: 8,
},
},
},
}
enum := Enum{
decl: decl{Name: fidlgen.MustReadName("example/Enum")},
Subtype: "uint16",
Members: []EnumMember{
{
member: member{Name: "ZERO"},
Value: "0",
},
},
}
bits := Bits{
decl: decl{Name: fidlgen.MustReadName("example/Bits")},
Subtype: "uint16",
Members: []BitsMember{
{
member: member{Name: "ONE"},
Index: 0,
},
},
}
expected := []Overlay{
{
decl: decl{
Name: fidlgen.MustReadName("example/BasicOverlay"),
Comments: []string{" This is an overlay."},
},
MaxVariantSize: 8,
Variants: []OverlayVariant{
{
member: member{
Name: "i64",
Comments: []string{" This is an overlay variant."},
},
Type: TypeDescriptor{
Type: "int64",
Kind: TypeKindInteger,
Size: 8,
},
Discriminant: 1,
},
{
member: member{Name: "u64"},
Type: TypeDescriptor{
Type: "uint64",
Kind: TypeKindInteger,
Size: 8,
},
Discriminant: 2,
},
{
member: member{Name: "i32"},
Type: TypeDescriptor{
Type: "int32",
Kind: TypeKindInteger,
Size: 4,
},
Discriminant: 3,
},
{
member: member{Name: "u32"},
Type: TypeDescriptor{
Type: "uint32",
Kind: TypeKindInteger,
Size: 4,
},
Discriminant: 4,
},
{
member: member{Name: "i16"},
Type: TypeDescriptor{
Type: "int16",
Kind: TypeKindInteger,
Size: 2,
},
Discriminant: 5,
},
{
member: member{Name: "u16"},
Type: TypeDescriptor{
Type: "uint16",
Kind: TypeKindInteger,
Size: 2,
},
Discriminant: 6,
},
{
member: member{Name: "i8"},
Type: TypeDescriptor{
Type: "int8",
Kind: TypeKindInteger,
Size: 1,
},
Discriminant: 7,
},
{
member: member{Name: "u8"},
Type: TypeDescriptor{
Type: "uint8",
Kind: TypeKindInteger,
Size: 1,
},
Discriminant: 8,
},
{
member: member{Name: "b"},
Type: TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
Discriminant: 9,
},
{
member: member{Name: "e"},
Type: TypeDescriptor{
Type: "example/Enum",
Kind: TypeKindEnum,
Decl: &enum,
Size: 2,
},
Discriminant: 10,
},
{
member: member{Name: "bits"},
Type: TypeDescriptor{
Type: "example/Bits",
Kind: TypeKindBits,
Decl: &bits,
Size: 2,
},
Discriminant: 11,
},
{
member: member{Name: "s"},
Type: TypeDescriptor{
Type: "example/OverlayStructVariant",
Kind: TypeKindStruct,
Decl: &structVariant,
Size: 8,
},
Discriminant: 12,
},
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
func TestCanSummarizeAliases(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
type Enum = enum : uint16 {
ZERO = 0;
};
type Bits = bits : uint16 {
ONE = 1;
};
type Struct = struct {
value uint64;
};
/// This is an alias.
alias Uint8Alias = uint8;
alias EnumAlias = Enum;
alias BitsAlias = Bits;
alias StructAlias = Struct;
alias ArrayAlias = array<uint32, 4>;
alias NestedArrayAlias = array<array<Struct, 8>, 4>;
// TODO(https://fxbug.dev/42057022, https://fxbug.dev/42172915): Aliases are currently broken.
// Exercise more complicated aliases (e.g., aliases of aliases) when fixed.
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
var actual []Alias
for _, decl := range summary.Files[0].Decls {
if decl.IsAlias() {
actual = append(actual, decl.AsAlias())
}
}
enum := Enum{
decl: decl{Name: fidlgen.MustReadName("example/Enum")},
Subtype: "uint16",
Members: []EnumMember{
{
member: member{Name: "ZERO"},
Value: "0",
},
},
}
bits := Bits{
decl: decl{Name: fidlgen.MustReadName("example/Bits")},
Subtype: "uint16",
Members: []BitsMember{
{
member: member{Name: "ONE"},
Index: 0,
},
},
}
strct := Struct{
decl: decl{Name: fidlgen.MustReadName("example/Struct")},
Size: 8,
Members: []StructMember{
{
member: member{Name: "value"},
Offset: 0,
Type: TypeDescriptor{
Type: "uint64",
Kind: TypeKindInteger,
Size: 8,
},
},
},
}
four, eight := 4, 8
expected := []Alias{
{
decl: decl{
Name: fidlgen.MustReadName("example/Uint8Alias"),
Comments: []string{" This is an alias."},
},
Value: TypeDescriptor{
Type: "uint8",
Kind: TypeKindInteger,
Size: 1,
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/EnumAlias")},
Value: TypeDescriptor{
Type: "example/Enum",
Kind: TypeKindEnum,
Decl: &enum,
Size: 2,
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/BitsAlias")},
Value: TypeDescriptor{
Type: "example/Bits",
Kind: TypeKindBits,
Decl: &bits,
Size: 2,
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/StructAlias")},
Value: TypeDescriptor{
Type: "example/Struct",
Kind: TypeKindStruct,
Decl: &strct,
Size: 8,
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/ArrayAlias")},
Value: TypeDescriptor{
Kind: TypeKindArray,
ElementType: &TypeDescriptor{
Type: "uint32",
Kind: TypeKindInteger,
Size: 4,
},
ElementCount: &four,
Size: 16,
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/NestedArrayAlias")},
Value: TypeDescriptor{
Kind: TypeKindArray,
ElementType: &TypeDescriptor{
Kind: TypeKindArray,
ElementType: &TypeDescriptor{
Type: "example/Struct",
Kind: TypeKindStruct,
Decl: &strct,
Size: 8,
},
ElementCount: &eight,
Size: 64,
},
ElementCount: &four,
Size: 256,
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
func TestCanSummarizeSyscalls(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
/// This gives the foo syscalls.
@transport("Syscall")
closed protocol Foo {
/// This is the foo_bar syscall.
strict Bar();
};
/// This
/// gives
/// the
/// fizz syscalls.
@no_protocol_prefix
@transport("Syscall")
closed protocol Arbitrary {
/// This
/// is
/// the
/// fizz_buzz syscall.
strict FizzBuzz();
};
@transport("Syscall")
closed protocol Category {
@vdsocall
strict VdsoCall();
@const
@vdsocall
strict ConstVdsoCall();
@internal
strict Internal();
@next
strict Next();
@blocking
strict Blocking();
@noreturn
strict NoReturn();
@testonly
strict Test0();
@testonly
@test_category1
strict Test1();
@testonly
@test_category2
strict Test2();
};
type StatusEnum = enum : uint32 {
OK = 0;
ERROR = 1;
};
type StructReturnType = struct {
value uint64;
};
@no_protocol_prefix
@transport("Syscall")
closed protocol SyscallWithParameters {
strict SyscallWithInputs(struct{
in1 uint64;
in2 bool;
});
strict SyscallWithOutputs() -> (struct{
out1 int32;
out2 uint16;
});
strict SyscallWithMixedOrientation(struct{
in uint64;
@inout
inout uint32;
@out
out1 uint32;
}) -> (struct{
out2 int8;
});
strict SyscallWithError(struct {
in bool;
}) -> (struct{
out bool;
}) error StatusEnum;
strict SyscallWithStructReturnType() -> (StructReturnType);
strict SyscallWithWrappedReturnType() -> (@wrapped_return struct {
value uint32;
});
};
`)
statusEnum := Enum{
decl: decl{Name: fidlgen.MustReadName("example/StatusEnum")},
Subtype: "uint32",
Members: []EnumMember{
{
member: member{Name: "OK"},
Value: "0",
},
{
member: member{Name: "ERROR"},
Value: "1",
},
},
}
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
// Normalize member order by name for a stable comparison.
var actual []SyscallFamily
for _, decl := range summary.Files[0].Decls {
if decl.IsSyscallFamily() {
fam := decl.AsSyscallFamily()
sort.Slice(fam.Syscalls, func(i, j int) bool {
return strings.Compare(fam.Syscalls[i].Name, fam.Syscalls[j].Name) < 0
})
actual = append(actual, fam)
}
}
expected := []SyscallFamily{
{
decl: decl{
Name: fidlgen.MustReadName("example/Foo"),
Comments: []string{" This gives the foo syscalls."},
},
Syscalls: []Syscall{
{
member: member{
Name: "FooBar",
Comments: []string{" This is the foo_bar syscall."},
},
},
},
},
{
decl: decl{
Name: fidlgen.MustReadName("example/Arbitrary"),
Comments: []string{" This", " gives", " the", " fizz syscalls."},
},
Syscalls: []Syscall{
{
member: member{
Name: "FizzBuzz",
Comments: []string{" This", " is", " the", " fizz_buzz syscall."},
},
},
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/Category")},
Syscalls: []Syscall{
{
member: member{Name: "CategoryBlocking"},
Blocking: true,
},
{
member: member{Name: "CategoryConstVdsoCall"},
Category: SyscallCategoryVdsoCall,
Const: true,
},
{
member: member{Name: "CategoryInternal"},
Category: SyscallCategoryInternal,
},
{
member: member{Name: "CategoryNext"},
Category: SyscallCategoryNext,
},
{
member: member{Name: "CategoryNoReturn"},
NoReturn: true,
},
{
member: member{Name: "CategoryTest0"},
Testonly: true,
},
{
member: member{Name: "CategoryTest1"},
Category: SyscallCategoryTest1,
Testonly: true,
},
{
member: member{Name: "CategoryTest2"},
Category: SyscallCategoryTest2,
Testonly: true,
},
{
member: member{Name: "CategoryVdsoCall"},
Category: SyscallCategoryVdsoCall,
},
},
},
{
decl: decl{Name: fidlgen.MustReadName("example/SyscallWithParameters")},
Syscalls: []Syscall{
{
member: member{Name: "SyscallWithError"},
Parameters: []SyscallParameter{
{
member: member{Name: "in"},
Type: TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
Orientation: ParameterOrientationIn,
},
{
member: member{Name: "out"},
Type: TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
Orientation: ParameterOrientationOut,
},
},
ReturnType: &TypeDescriptor{
Type: "example/StatusEnum",
Kind: TypeKindEnum,
Decl: &statusEnum,
Size: 4,
},
},
{
member: member{Name: "SyscallWithInputs"},
Parameters: []SyscallParameter{
{
member: member{Name: "in1"},
Type: TypeDescriptor{
Type: "uint64",
Kind: TypeKindInteger,
Size: 8,
},
Orientation: ParameterOrientationIn,
},
{
member: member{Name: "in2"},
Type: TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
Orientation: ParameterOrientationIn,
},
},
},
{
member: member{Name: "SyscallWithMixedOrientation"},
Parameters: []SyscallParameter{
{
member: member{Name: "in"},
Type: TypeDescriptor{
Type: "uint64",
Kind: TypeKindInteger,
Size: 8,
},
Orientation: ParameterOrientationIn,
},
{
member: member{Name: "inout"},
Type: TypeDescriptor{
Type: "uint32",
Kind: TypeKindInteger,
Size: 4,
},
Orientation: ParameterOrientationInOut,
},
{
member: member{Name: "out1"},
Type: TypeDescriptor{
Type: "uint32",
Kind: TypeKindInteger,
Size: 4,
},
Orientation: ParameterOrientationOut,
},
{
member: member{Name: "out2"},
Type: TypeDescriptor{
Type: "int8",
Kind: TypeKindInteger,
Size: 1,
},
Orientation: ParameterOrientationOut,
},
},
},
{
member: member{Name: "SyscallWithOutputs"},
Parameters: []SyscallParameter{
{
member: member{Name: "out1"},
Type: TypeDescriptor{
Type: "int32",
Kind: TypeKindInteger,
Size: 4,
},
Orientation: ParameterOrientationOut,
},
{
member: member{Name: "out2"},
Type: TypeDescriptor{
Type: "uint16",
Kind: TypeKindInteger,
Size: 2,
},
Orientation: ParameterOrientationOut,
},
},
},
{
member: member{Name: "SyscallWithStructReturnType"},
ReturnType: &TypeDescriptor{
Type: "example/StructReturnType",
Kind: TypeKindStruct,
Decl: &Struct{
decl: decl{Name: fidlgen.MustReadName("example/StructReturnType")},
Members: []StructMember{
{
member: member{Name: "value"},
Type: TypeDescriptor{
Type: "uint64",
Kind: TypeKindInteger,
Size: 8,
},
},
},
Size: 8,
},
Size: 8,
},
},
{
member: member{Name: "SyscallWithWrappedReturnType"},
ReturnType: &TypeDescriptor{
Type: "uint32",
Kind: TypeKindInteger,
Size: 4,
},
},
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
// TODO(https://fxbug.dev/42057022, https://fxbug.dev/42065140): Tests a workaround for these bugs,
// needed until one of them is fixed.
func TestCanSummarizeSyscallsWithZxStatusErrors(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library zx;
alias Status = int32;
@transport("Syscall")
closed protocol Foo {
strict WithStatus(struct {
in bool;
}) -> (struct{
out bool;
}) error Status;
};
`)
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
var actual []SyscallFamily
for _, decl := range summary.Files[0].Decls {
if decl.IsSyscallFamily() {
actual = append(actual, decl.AsSyscallFamily())
}
}
expected := []SyscallFamily{
{
decl: decl{Name: fidlgen.MustReadName("zx/Foo")},
Syscalls: []Syscall{
{
member: member{Name: "FooWithStatus"},
Parameters: []SyscallParameter{
{
member: member{Name: "in"},
Type: TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
Orientation: ParameterOrientationIn,
},
{
member: member{Name: "out"},
Type: TypeDescriptor{
Type: "bool",
Kind: TypeKindBool,
Size: 1,
},
Orientation: ParameterOrientationOut,
},
},
ReturnType: &TypeDescriptor{
Type: "zx/Status",
Kind: TypeKindAlias,
Decl: &Alias{
decl: decl{Name: fidlgen.MustReadName("zx/Status")},
Value: TypeDescriptor{
Type: "int32",
Kind: TypeKindInteger,
Size: 4,
},
},
Size: 4,
},
},
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}
func TestCanSummarizeSyscallsWithVectors(t *testing.T) {
wd := t.TempDir()
ir := fidlgentest.EndToEndTest{T: t}.WithWorkingDirectory(wd).Single(`
library example;
type EmptyStruct = struct {};
@transport("Syscall")
closed protocol Syscall {
strict WithVectors(struct{
structs vector<EmptyStruct>;
@size32
u16_vec32 vector<uint16>;
@voidptr
void_vec vector<uint8>;
@size32
@voidptr
void_vec32 vector<uint8>;
@inout
inout_i8s vector<int8>;
@inout
@size32
inout_i8_vec32 vector<int8>;
}) -> (struct {
out_bools vector<bool>;
});
};
`)
emptyStruct := Struct{
decl: decl{Name: fidlgen.MustReadName("example/EmptyStruct")},
Size: 1,
}
summary, err := Summarize(ir, wd, SourceDeclOrder, func(Decl) {})
if err != nil {
t.Fatal(err)
}
var actual []SyscallFamily
for _, decl := range summary.Files[0].Decls {
if decl.IsSyscallFamily() {
actual = append(actual, decl.AsSyscallFamily())
}
}
expected := []SyscallFamily{
{
decl: decl{Name: fidlgen.MustReadName("example/Syscall")},
Syscalls: []Syscall{
{
member: member{Name: "SyscallWithVectors"},
Parameters: []SyscallParameter{
{
member: member{Name: "structs"},
Type: TypeDescriptor{
Kind: TypeKindPointer,
ElementType: &TypeDescriptor{
Kind: TypeKindStruct,
Type: "example/EmptyStruct",
Decl: &emptyStruct,
Size: 1,
},
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "num_structs"},
Type: TypeDescriptor{
Kind: TypeKindSize,
Type: "usize64",
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "u16_vec32"},
Type: TypeDescriptor{
Kind: TypeKindPointer,
ElementType: &TypeDescriptor{
Kind: TypeKindInteger,
Type: "uint16",
Size: 2,
},
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "u16_vec32_size"},
Type: TypeDescriptor{
Kind: TypeKindInteger,
Type: "uint32",
Size: 4,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "void_vec"},
Type: TypeDescriptor{
Kind: TypeKindVoidPointer,
ElementType: &TypeDescriptor{
Kind: TypeKindInteger,
Type: "uint8",
Size: 1,
},
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "void_vec_size"},
Type: TypeDescriptor{
Kind: TypeKindSize,
Type: "usize64",
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "void_vec32"},
Type: TypeDescriptor{
Kind: TypeKindVoidPointer,
ElementType: &TypeDescriptor{
Kind: TypeKindInteger,
Type: "uint8",
Size: 1,
},
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "void_vec32_size"},
Type: TypeDescriptor{
Kind: TypeKindInteger,
Type: "uint32",
Size: 4,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "inout_i8s"},
Type: TypeDescriptor{
Kind: TypeKindPointer,
ElementType: &TypeDescriptor{
Kind: TypeKindInteger,
Type: "int8",
Size: 1,
Mutable: true,
},
Size: 8,
},
Orientation: ParameterOrientationInOut,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "num_inout_i8s"},
Type: TypeDescriptor{
Kind: TypeKindSize,
Type: "usize64",
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "inout_i8_vec32"},
Type: TypeDescriptor{
Kind: TypeKindPointer,
ElementType: &TypeDescriptor{
Kind: TypeKindInteger,
Type: "int8",
Size: 1,
Mutable: true,
},
Size: 8,
},
Orientation: ParameterOrientationInOut,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "inout_i8_vec32_size"},
Type: TypeDescriptor{
Kind: TypeKindInteger,
Type: "uint32",
Size: 4,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "out_bools"},
Type: TypeDescriptor{
Kind: TypeKindPointer,
ElementType: &TypeDescriptor{
Kind: TypeKindBool,
Type: "bool",
Size: 1,
Mutable: true,
},
Size: 8,
},
Orientation: ParameterOrientationOut,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
{
member: member{Name: "num_out_bools"},
Type: TypeDescriptor{
Kind: TypeKindSize,
Type: "usize64",
Size: 8,
},
Orientation: ParameterOrientationIn,
Tags: map[ParameterTag]struct{}{
ParameterTagDecayedFromVector: {},
},
},
},
},
},
},
}
if diff := cmp.Diff(expected, actual, cmpOpt); diff != "" {
t.Error(diff)
}
}