blob: ceb600033478141b1e4255b414c5d4bab5b21ba1 [file] [log] [blame]
// Copyright 2020 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 codegen
import (
"testing"
"github.com/google/go-cmp/cmp"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgentest"
)
func TestDerivesToString(t *testing.T) {
cases := []struct {
input derives
expected string
}{
{0, ""},
{derivesDebug, "#[derive(Debug)]"},
{derivesPartialOrd, "#[derive(PartialOrd)]"},
{derivesHash | derivesAsBytes, "#[derive(Hash, zerocopy::AsBytes)]"},
}
for _, ex := range cases {
actual := ex.input.String()
if actual != ex.expected {
t.Errorf("%d: expected '%s', actual '%s'", ex.input, ex.expected, actual)
}
}
}
func TestBuildPaddingMarkersWithoutFlattening(t *testing.T) {
type testCase struct {
name string
in fidlgen.Struct
out []PaddingMarker
}
testCases := []testCase{
{
name: "empty",
in: fidlgen.Struct{},
out: nil,
},
{
name: "no padding 8-bytes",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 8,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 4,
Padding: 0,
},
},
},
},
out: nil,
},
{
name: "no padding 4-bytes",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 4,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 2,
Padding: 0,
},
},
},
},
out: nil,
},
{
name: "no padding 2-bytes",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 2,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 1,
Padding: 0,
},
},
},
},
out: nil,
},
{
name: "no padding 1-byte",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 1,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
},
},
out: nil,
},
{
name: "8-byte struct with 2 bytes of padding at end",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 8,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 4,
Padding: 2,
},
},
},
},
out: []PaddingMarker{
{
Type: "u64",
Offset: 0,
Mask: "0xffff000000000000u64",
},
},
},
{
name: "4-byte struct with 1 byte of padding at end",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 4,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 2,
Padding: 1,
},
},
},
},
out: []PaddingMarker{
{
Type: "u32",
Offset: 0,
Mask: "0xff000000u32",
},
},
},
{
name: "2-byte struct with 1 byte padding at end",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 2,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 1,
},
},
},
},
out: []PaddingMarker{
{
Type: "u16",
Offset: 0,
Mask: "0xff00u16",
},
},
},
{
name: "padding at end of 8-byte chunk, before next chunk",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 16,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 4,
Padding: 2,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 8,
Padding: 0,
},
},
},
},
out: []PaddingMarker{
{
Type: "u64",
Offset: 0,
Mask: "0xffff000000000000u64",
},
},
},
{
name: "padding in middle of 4-byte block",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 4,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 1,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 2,
Padding: 0,
},
},
},
},
out: []PaddingMarker{
{
Type: "u32",
Offset: 0,
Mask: "0x0000ff00u32",
},
},
},
{
name: "8 byte mask with non-zero offset",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 16,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 8,
Padding: 4,
},
},
},
},
out: []PaddingMarker{
{
Type: "u64",
Offset: 8,
Mask: "0xffffffff00000000u64",
},
},
},
{
name: "4 byte mask with non-zero offset",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 12,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 8,
Padding: 2,
},
},
},
},
out: []PaddingMarker{
{
Type: "u32",
Offset: 8,
Mask: "0xffff0000u32",
},
},
},
{
name: "2 byte mask with non-zero offset",
in: fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 10,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
},
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 8,
Padding: 1,
},
},
},
},
out: []PaddingMarker{
{
Type: "u16",
Offset: 8,
Mask: "0xff00u16",
},
},
},
}
for _, testCase := range testCases {
c := compiler{}
out := c.buildPaddingMarkers(testCase.in, false)
if diff := cmp.Diff(testCase.out, out); diff != "" {
t.Errorf("%s:\nexpected != actual (-want +got)\n%s", testCase.name, diff)
}
}
}
func TestBuildPaddingMarkersFlatteningStruct(t *testing.T) {
var innerStructIdentifier fidlgen.EncodedCompoundIdentifier = "abcd"
innerStruct := fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 4,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 3,
},
},
},
}
input := fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 8,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 4,
},
Type: fidlgen.Type{
Kind: fidlgen.IdentifierType,
Identifier: innerStructIdentifier,
},
},
},
}
resourceness := fidlgen.IsValueType
c := compiler{
decls: map[fidlgen.EncodedCompoundIdentifier]fidlgen.DeclInfo{
innerStructIdentifier: {Type: fidlgen.StructDeclType, Resourceness: &resourceness},
},
structs: map[fidlgen.EncodedCompoundIdentifier]fidlgen.Struct{
innerStructIdentifier: innerStruct,
},
library: fidlgen.LibraryIdentifier{""},
}
out := c.buildPaddingMarkers(input, true)
expected := []PaddingMarker{
{
Type: "u64",
Offset: 0,
Mask: "0xffffffffffffff00u64",
},
}
if diff := cmp.Diff(expected, out); diff != "" {
t.Errorf("expected != actual (-want +got)\n%s", diff)
}
}
func TestBuildPaddingMarkersFlatteningArray(t *testing.T) {
var innerStructIdentifier fidlgen.EncodedCompoundIdentifier = "abcd"
innerStruct := fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 4,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 3,
},
},
},
}
count := 3
input := fidlgen.Struct{
TypeShapeV1: fidlgen.TypeShape{
InlineSize: 12,
},
Members: []fidlgen.StructMember{
{
FieldShapeV1: fidlgen.FieldShape{
Offset: 0,
Padding: 0,
},
Type: fidlgen.Type{
Kind: fidlgen.ArrayType,
ElementCount: &count,
ElementType: &fidlgen.Type{
Kind: fidlgen.IdentifierType,
Identifier: innerStructIdentifier,
},
},
},
},
}
resourceness := fidlgen.IsValueType
c := compiler{
decls: map[fidlgen.EncodedCompoundIdentifier]fidlgen.DeclInfo{
innerStructIdentifier: {Type: fidlgen.StructDeclType, Resourceness: &resourceness},
},
structs: map[fidlgen.EncodedCompoundIdentifier]fidlgen.Struct{
innerStructIdentifier: innerStruct,
},
library: fidlgen.LibraryIdentifier{""},
}
out := c.buildPaddingMarkers(input, true)
expected := []PaddingMarker{
{
Type: "u64",
Offset: 0,
Mask: "0xffffff00ffffff00u64",
},
{
Type: "u32",
Offset: 8,
Mask: "0xffffff00u32",
},
}
if diff := cmp.Diff(expected, out); diff != "" {
t.Errorf("expected != actual (-want +got)\n%s", diff)
}
}
func TestDerivesCalculation(t *testing.T) {
cases := []struct {
fidl string
expected string
}{
{
fidl: `struct MyStruct { string field; };`,
expected: "#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]",
},
{
fidl: `struct MyStruct { float32 field; };`,
expected: "#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]",
},
{
fidl: `resource struct MyStruct {};`,
expected: "#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, zerocopy::AsBytes, zerocopy::FromBytes)]",
},
}
for _, ex := range cases {
root := Compile(fidlgentest.EndToEndTest{T: t}.Single(`library example; ` + ex.fidl))
actual := root.Structs[0].Derives.String()
if ex.expected != actual {
t.Errorf("%s: expected %s, found %s", ex.fidl, ex.expected, actual)
}
}
}