// Copyright 2019 The Amber 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.

#include "src/format.h"

#include "gtest/gtest.h"
#include "src/make_unique.h"
#include "src/type_parser.h"

namespace amber {

using FormatTest = testing::Test;

TEST_F(FormatTest, DefaultToStd430) {
  auto f32 = type::Number::Float(32);
  Format fmt(f32.get());
  EXPECT_EQ(Format::Layout::kStd430, fmt.GetLayout());
}

TEST_F(FormatTest, SizeInBytesVector_Std430) {
  TypeParser parser;
  auto type = parser.Parse("R32G32B32_SFLOAT");
  ASSERT_TRUE(type != nullptr);

  Format fmt(type.get());
  EXPECT_EQ(3U, fmt.InputNeededPerElement());
  EXPECT_EQ(16U, fmt.SizeInBytes());
}

TEST_F(FormatTest, SizeInBytesMatrix_Std430) {
  TypeParser parser;
  auto type = parser.Parse("R32G32B32_SFLOAT");
  ASSERT_TRUE(type != nullptr);
  type->SetColumnCount(3);

  Format fmt(type.get());
  EXPECT_EQ(9U, fmt.InputNeededPerElement());
  EXPECT_EQ(48U, fmt.SizeInBytes());
}

TEST_F(FormatTest, SizeInBytesMatrix_Std140) {
  TypeParser parser;
  auto type = parser.Parse("R32G32_SFLOAT");
  ASSERT_TRUE(type != nullptr);
  type->SetColumnCount(2);

  Format fmt(type.get());
  fmt.SetLayout(Format::Format::Layout::kStd140);
  EXPECT_EQ(32U, fmt.SizeInBytes());
}

struct StdData {
  const char* name;
  const char* fmt;
  uint32_t column_count;
  bool is_std140;
  uint32_t size_in_bytes;
};
using FormatStdTest = testing::TestWithParam<StdData>;
TEST_P(FormatStdTest, Test) {
  auto test_data = GetParam();

  TypeParser parser;
  auto type = parser.Parse(test_data.fmt);
  ASSERT_TRUE(type != nullptr) << test_data.name;

  type->SetColumnCount(test_data.column_count);

  Format fmt(type.get());
  if (test_data.is_std140)
    fmt.SetLayout(Format::Format::Layout::kStd140);

  EXPECT_EQ(test_data.size_in_bytes, fmt.SizeInBytes()) << test_data.name;
}

INSTANTIATE_TEST_SUITE_P(
    FormatStdTestSamples,
    FormatStdTest,
    testing::Values(
        StdData{"mat2x2-std140", "R32G32_SFLOAT", 2, true, 32U},
        StdData{"mat2x3-std140", "R32G32B32_SFLOAT", 2, true, 32U},
        StdData{"mat2x4-std140", "R32G32B32A32_SFLOAT", 2, true, 32U},
        StdData{"mat3x2-std140", "R32G32_SFLOAT", 3, true, 48U},
        StdData{"mat3x3-std140", "R32G32B32_SFLOAT", 3, true, 48U},
        StdData{"mat3x4-std140", "R32G32B32A32_SFLOAT", 3, true, 48U},
        StdData{"mat4x2-std140", "R32G32_SFLOAT", 4, true, 64U},
        StdData{"mat4x3-std140", "R32G32B32_SFLOAT", 4, true, 64U},
        StdData{"mat4x4-std140", "R32G32B32A32_SFLOAT", 4, true, 64U},
        StdData{"mat2x2-std430", "R32G32_SFLOAT", 2, false, 16U},
        StdData{"mat2x3-std430", "R32G32B32_SFLOAT", 2, false, 32U},
        StdData{"mat2x4-std430", "R32G32B32A32_SFLOAT", 2, false, 32U},
        StdData{"mat3x2-std430", "R32G32_SFLOAT", 3, false, 24U},
        StdData{"mat3x3-std430", "R32G32B32_SFLOAT", 3, false, 48U},
        StdData{"mat3x4-std430", "R32G32B32A32_SFLOAT", 3, false, 48U},
        StdData{"mat4x2-std430", "R32G32_SFLOAT", 4, false, 32U},
        StdData{"mat4x3-std430", "R32G32B32_SFLOAT", 4, false, 64U},
        StdData{"mat4x4-std430", "R32G32B32A32_SFLOAT", 4, false, 64U},
        StdData{"float-std140", "R32_SFLOAT", 1, true, 4U},
        StdData{"float-std430", "R32_SFLOAT", 1, false,
                4U}));  // NOLINT(whitespace/parens)

struct Name {
  const char* name;
};
using FormatNameTest = testing::TestWithParam<Name>;

TEST_P(FormatNameTest, Test) {
  auto test_data = GetParam();

  TypeParser parser;
  auto type = parser.Parse(test_data.name);
  ASSERT_TRUE(type != nullptr) << test_data.name;

  Format fmt(type.get());
  EXPECT_EQ(test_data.name, fmt.GenerateNameForTesting());
}
INSTANTIATE_TEST_SUITE_P(
    FormatNameGenerateTest,
    FormatNameTest,
    testing::Values(Name{"A1R5G5B5_UNORM_PACK16"},
                    Name{"A2B10G10R10_SINT_PACK32"},
                    Name{"A2B10G10R10_SNORM_PACK32"},
                    Name{"A2B10G10R10_SSCALED_PACK32"},
                    Name{"A2B10G10R10_UINT_PACK32"},
                    Name{"A2B10G10R10_UNORM_PACK32"},
                    Name{"A2B10G10R10_USCALED_PACK32"},
                    Name{"A2R10G10B10_SINT_PACK32"},
                    Name{"A2R10G10B10_SNORM_PACK32"},
                    Name{"A2R10G10B10_SSCALED_PACK32"},
                    Name{"A2R10G10B10_UINT_PACK32"},
                    Name{"A2R10G10B10_UNORM_PACK32"},
                    Name{"A2R10G10B10_USCALED_PACK32"},
                    Name{"A8B8G8R8_SINT_PACK32"},
                    Name{"A8B8G8R8_SNORM_PACK32"},
                    Name{"A8B8G8R8_SRGB_PACK32"},
                    Name{"A8B8G8R8_SSCALED_PACK32"},
                    Name{"A8B8G8R8_UINT_PACK32"},
                    Name{"A8B8G8R8_UNORM_PACK32"},
                    Name{"A8B8G8R8_USCALED_PACK32"},
                    Name{"B10G11R11_UFLOAT_PACK32"},
                    Name{"B4G4R4A4_UNORM_PACK16"},
                    Name{"B5G5R5A1_UNORM_PACK16"},
                    Name{"B5G6R5_UNORM_PACK16"},
                    Name{"B8G8R8A8_SINT"},
                    Name{"B8G8R8A8_SNORM"},
                    Name{"B8G8R8A8_SRGB"},
                    Name{"B8G8R8A8_SSCALED"},
                    Name{"B8G8R8A8_UINT"},
                    Name{"B8G8R8A8_UNORM"},
                    Name{"B8G8R8A8_USCALED"},
                    Name{"B8G8R8_SINT"},
                    Name{"B8G8R8_SNORM"},
                    Name{"B8G8R8_SRGB"},
                    Name{"B8G8R8_SSCALED"},
                    Name{"B8G8R8_UINT"},
                    Name{"B8G8R8_UNORM"},
                    Name{"B8G8R8_USCALED"},
                    Name{"D16_UNORM"},
                    Name{"D16_UNORM_S8_UINT"},
                    Name{"D24_UNORM_S8_UINT"},
                    Name{"D32_SFLOAT"},
                    Name{"D32_SFLOAT_S8_UINT"},
                    Name{"R16G16B16A16_SFLOAT"},
                    Name{"R16G16B16A16_SINT"},
                    Name{"R16G16B16A16_SNORM"},
                    Name{"R16G16B16A16_SSCALED"},
                    Name{"R16G16B16A16_UINT"},
                    Name{"R16G16B16A16_UNORM"},
                    Name{"R16G16B16A16_USCALED"},
                    Name{"R16G16B16_SFLOAT"},
                    Name{"R16G16B16_SINT"},
                    Name{"R16G16B16_SNORM"},
                    Name{"R16G16B16_SSCALED"},
                    Name{"R16G16B16_UINT"},
                    Name{"R16G16B16_UNORM"},
                    Name{"R16G16B16_USCALED"},
                    Name{"R16G16_SFLOAT"},
                    Name{"R16G16_SINT"},
                    Name{"R16G16_SNORM"},
                    Name{"R16G16_SSCALED"},
                    Name{"R16G16_UINT"},
                    Name{"R16G16_UNORM"},
                    Name{"R16G16_USCALED"},
                    Name{"R16_SFLOAT"},
                    Name{"R16_SINT"},
                    Name{"R16_SNORM"},
                    Name{"R16_SSCALED"},
                    Name{"R16_UINT"},
                    Name{"R16_UNORM"},
                    Name{"R16_USCALED"},
                    Name{"R32G32B32A32_SFLOAT"},
                    Name{"R32G32B32A32_SINT"},
                    Name{"R32G32B32A32_UINT"},
                    Name{"R32G32B32_SFLOAT"},
                    Name{"R32G32B32_SINT"},
                    Name{"R32G32B32_UINT"},
                    Name{"R32G32_SFLOAT"},
                    Name{"R32G32_SINT"},
                    Name{"R32G32_UINT"},
                    Name{"R32_SFLOAT"},
                    Name{"R32_SINT"},
                    Name{"R32_UINT"},
                    Name{"R4G4B4A4_UNORM_PACK16"},
                    Name{"R4G4_UNORM_PACK8"},
                    Name{"R5G5B5A1_UNORM_PACK16"},
                    Name{"R5G6B5_UNORM_PACK16"},
                    Name{"R64G64B64A64_SFLOAT"},
                    Name{"R64G64B64A64_SINT"},
                    Name{"R64G64B64A64_UINT"},
                    Name{"R64G64B64_SFLOAT"},
                    Name{"R64G64B64_SINT"},
                    Name{"R64G64B64_UINT"},
                    Name{"R64G64_SFLOAT"},
                    Name{"R64G64_SINT"},
                    Name{"R64G64_UINT"},
                    Name{"R64_SFLOAT"},
                    Name{"R64_SINT"},
                    Name{"R64_UINT"},
                    Name{"R8G8B8A8_SINT"},
                    Name{"R8G8B8A8_SNORM"},
                    Name{"R8G8B8A8_SRGB"},
                    Name{"R8G8B8A8_SSCALED"},
                    Name{"R8G8B8A8_UINT"},
                    Name{"R8G8B8A8_UNORM"},
                    Name{"R8G8B8A8_USCALED"},
                    Name{"R8G8B8_SINT"},
                    Name{"R8G8B8_SNORM"},
                    Name{"R8G8B8_SRGB"},
                    Name{"R8G8B8_SSCALED"},
                    Name{"R8G8B8_UINT"},
                    Name{"R8G8B8_UNORM"},
                    Name{"R8G8B8_USCALED"},
                    Name{"R8G8_SINT"},
                    Name{"R8G8_SNORM"},
                    Name{"R8G8_SRGB"},
                    Name{"R8G8_SSCALED"},
                    Name{"R8G8_UINT"},
                    Name{"R8G8_UNORM"},
                    Name{"R8G8_USCALED"},
                    Name{"R8_SINT"},
                    Name{"R8_SNORM"},
                    Name{"R8_SRGB"},
                    Name{"R8_SSCALED"},
                    Name{"R8_UINT"},
                    Name{"R8_UNORM"},
                    Name{"R8_USCALED"},
                    Name{"S8_UINT"},
                    Name{"X8_D24_UNORM_PACK32"}));  // NOLINT(whitespace/parens)

TEST_F(FormatTest, SegmentPackedList_Std430) {
  TypeParser parser;
  auto type = parser.Parse("A8B8G8R8_SINT_PACK32");

  Format fmt(type.get());
  EXPECT_EQ(4u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(1U, segs.size());
  // Always packs into a unsigned ...
  EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
}

TEST_F(FormatTest, SegmentListR32G32_Std140) {
  TypeParser parser;
  auto type = parser.Parse("R32G32_UINT");

  Format fmt(type.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(8u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(2U, segs.size());
  EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
}

TEST_F(FormatTest, SegmentListR32G32B32_Std140) {
  TypeParser parser;
  auto type = parser.Parse("R32G32B32_UINT");

  Format fmt(type.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(16u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(4U, segs.size());
  EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_TRUE(segs[3].IsPadding());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
}

TEST_F(FormatTest, SegmentListR32G32B32_Std430) {
  TypeParser parser;
  auto type = parser.Parse("R32G32B32_UINT");

  Format fmt(type.get());
  fmt.SetLayout(Format::Format::Layout::kStd430);
  EXPECT_EQ(16u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(4U, segs.size());
  EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_TRUE(segs[3].IsPadding());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
}

TEST_F(FormatTest, SegmentMat2x2_Std140) {
  TypeParser parser;
  auto type = parser.Parse("R32G32_SFLOAT");
  type->SetColumnCount(2);

  Format fmt(type.get());
  fmt.SetLayout(Format::Format::Layout::kStd140);
  EXPECT_EQ(32u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(6U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_TRUE(segs[2].IsPadding());
  EXPECT_EQ(8u, segs[2].SizeInBytes());

  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  EXPECT_TRUE(segs[5].IsPadding());
  EXPECT_EQ(8u, segs[5].SizeInBytes());
}

TEST_F(FormatTest, SegmentMat2x2_Std430) {
  TypeParser parser;
  auto type = parser.Parse("R32G32_SFLOAT");
  type->SetColumnCount(2);

  Format fmt(type.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(16u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(4U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());

  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
}

TEST_F(FormatTest, SegmentMat2x3_Std430) {
  TypeParser parser;
  auto type = parser.Parse("R32G32B32_SFLOAT");
  type->SetColumnCount(2);

  Format fmt(type.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(32u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(8U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_TRUE(segs[3].IsPadding());
  EXPECT_EQ(4u, segs[3].SizeInBytes());

  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
  EXPECT_EQ(4u, segs[6].SizeInBytes());
  EXPECT_TRUE(segs[7].IsPadding());
  EXPECT_EQ(4u, segs[7].SizeInBytes());
}

TEST_F(FormatTest, SegmentRuntimeArray_Std140) {
  TypeParser parser;
  auto type = parser.Parse("R32_SFLOAT");
  type->SetIsRuntimeArray();

  Format fmt(type.get());
  fmt.SetLayout(Format::Format::Layout::kStd140);
  EXPECT_EQ(16u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(2U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
}

TEST_F(FormatTest, SegmentRuntimeArray_Std430) {
  TypeParser parser;
  auto type = parser.Parse("R32_SFLOAT");
  type->SetIsRuntimeArray();

  Format fmt(type.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(4u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(1U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
}

// struct {
//  float x;
//  int32 y;
// }
TEST_F(FormatTest, SegmentStruct_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto u32 = type::Number::Uint(32);
  s->AddMember(f32.get());
  s->AddMember(u32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(16u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(3u, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_TRUE(segs[2].IsPadding());
  EXPECT_EQ(8u, segs[2].SizeInBytes());
}
TEST_F(FormatTest, SegmentStruct_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto u32 = type::Number::Uint(32);
  s->AddMember(f32.get());
  s->AddMember(u32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(8u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(2U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
}

// struct STRIDE 20 {
//  float x;
//  int32 y;
// }
// Note, the STRIDE is the stride over the entire structure.
TEST_F(FormatTest, SegmentStructWithStride_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto u32 = type::Number::Uint(32);
  s->AddMember(f32.get());
  s->AddMember(u32.get());
  s->SetStrideInBytes(20);

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(20u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(3U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_TRUE(segs[2].IsPadding());
  EXPECT_EQ((20u - sizeof(float) - sizeof(uint32_t)), segs[2].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStride_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto u32 = type::Number::Uint(32);
  s->AddMember(f32.get());
  s->AddMember(u32.get());
  s->SetStrideInBytes(20);

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(20u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(3U, segs.size());
  EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
  EXPECT_EQ(32U, segs[0].GetNumBits());
  EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
  EXPECT_EQ(32U, segs[1].GetNumBits());
  EXPECT_TRUE(segs[2].IsPadding());
  EXPECT_EQ((20u - sizeof(float) - sizeof(uint32_t)) * 8, segs[2].GetNumBits());
}

// struct {
//  float x OFFSET 4;
//  int32 y;
// }
TEST_F(FormatTest, SegmentStructWithMemberOffset_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto u32 = type::Number::Uint(32);

  auto m = s->AddMember(f32.get());
  m->offset_in_bytes = 4;

  s->AddMember(u32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(16u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(4U, segs.size());
  EXPECT_TRUE(segs[0].IsPadding());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_TRUE(segs[3].IsPadding());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithMemberOffset_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto u32 = type::Number::Uint(32);

  auto m = s->AddMember(f32.get());
  m->offset_in_bytes = 4;

  s->AddMember(u32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(12, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(3U, segs.size());
  EXPECT_TRUE(segs[0].IsPadding());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
}

// struct {
//   struct {
//     int32 a;
//     float b;
//   } x;
//   float y;
// }
TEST_F(FormatTest, SegmentStructWithStruct_Std140) {
  auto x = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  x->AddMember(i32.get());
  x->AddMember(f32.get());

  auto s = MakeUnique<type::Struct>();
  s->AddMember(x.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(32u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(5U, segs.size());
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_TRUE(segs[2].IsPadding());
  EXPECT_EQ(8u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  EXPECT_TRUE(segs[4].IsPadding());
  EXPECT_EQ(12u, segs[4].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStruct_Std430) {
  auto x = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  x->AddMember(i32.get());
  x->AddMember(f32.get());

  auto s = MakeUnique<type::Struct>();
  s->AddMember(x.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(12u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(3U, segs.size());
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
}

// struct {
//   int32 w;
//   vec2<float> x;
//   float y;
// }
TEST_F(FormatTest, SegmentStructWithVec2_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto vec2 = type::Number::Float(32);
  vec2->SetRowCount(2);

  s->AddMember(i32.get());
  s->AddMember(vec2.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(32u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(6U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  /* vec2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[5].IsPadding());
  EXPECT_EQ(12u, segs[5].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithVec2_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto vec2 = type::Number::Float(32);
  vec2->SetRowCount(2);

  s->AddMember(i32.get());
  s->AddMember(vec2.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(24u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(6U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  /* vec2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[5].IsPadding());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
}

// struct {
//   int32 w;
//   vec3<float> x;
//   float y;
// }
TEST_F(FormatTest, SegmentStructWithFloatPackedToVec_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto vec3 = type::Number::Float(32);
  vec3->SetRowCount(3);

  s->AddMember(i32.get());
  s->AddMember(vec3.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(32u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(6U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
  /* vec2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithFloatPackedToVec_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto vec3 = type::Number::Float(32);
  vec3->SetRowCount(3);

  s->AddMember(i32.get());
  s->AddMember(vec3.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(32u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(6U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
  /* vec2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
}

// struct {
//   int32 w;
//   vec3<float> x;
//   vec2<float> y;
// }
TEST_F(FormatTest, SegmentStructVec3Vec2_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto i32 = type::Number::Int(32);
  auto vec3 = type::Number::Float(32);
  vec3->SetRowCount(3);
  auto vec2 = type::Number::Float(32);
  vec2->SetRowCount(2);

  s->AddMember(i32.get());
  s->AddMember(vec3.get());
  s->AddMember(vec2.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(48u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(9U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
  /* vec3 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[5].IsPadding());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  /* vec2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
  EXPECT_EQ(4u, segs[6].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[7].GetFormatMode());
  EXPECT_EQ(4u, segs[7].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[8].IsPadding());
  EXPECT_EQ(8u, segs[8].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructVec3Vec2_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto i32 = type::Number::Int(32);
  auto vec3 = type::Number::Float(32);
  vec3->SetRowCount(3);
  auto vec2 = type::Number::Float(32);
  vec2->SetRowCount(2);

  s->AddMember(i32.get());
  s->AddMember(vec3.get());
  s->AddMember(vec2.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(48u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(9U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
  /* vec3 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[5].IsPadding());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  /* vec2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
  EXPECT_EQ(4u, segs[6].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[7].GetFormatMode());
  EXPECT_EQ(4u, segs[7].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[8].IsPadding());
  EXPECT_EQ(8u, segs[8].SizeInBytes());
}

// struct {
//   int32 w;
//   mat2x2<float> x;
//   float y;
// }
TEST_F(FormatTest, SegmentStructMat2x2_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto mat2x2 = type::Number::Float(32);
  mat2x2->SetRowCount(2);
  mat2x2->SetColumnCount(2);

  s->AddMember(i32.get());
  s->AddMember(mat2x2.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(64u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(10U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
  /* column 1 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  EXPECT_TRUE(segs[4].IsPadding());
  EXPECT_EQ(8u, segs[4].SizeInBytes());
  /* column 2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
  EXPECT_EQ(4u, segs[6].SizeInBytes());
  EXPECT_TRUE(segs[7].IsPadding());
  EXPECT_EQ(8u, segs[7].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[8].GetFormatMode());
  EXPECT_EQ(4u, segs[8].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[9].IsPadding());
  EXPECT_EQ(12u, segs[9].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructMat2x2_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto mat2x2 = type::Number::Float(32);
  mat2x2->SetRowCount(2);
  mat2x2->SetColumnCount(2);

  s->AddMember(i32.get());
  s->AddMember(mat2x2.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(32u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(8U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  /* column 1 */
  EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  /* column 2 */
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
  EXPECT_EQ(4u, segs[6].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[7].IsPadding());
  EXPECT_EQ(4u, segs[7].SizeInBytes());
}

// struct {
//   int32 w;
//   struct {
//     int32 a;
//     int32 b;
//     float c;
//   } x;
//   float y;
// }
TEST_F(FormatTest, SegmentStructWithStructNoPack_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto x = MakeUnique<type::Struct>();
  x->AddMember(i32.get());
  x->AddMember(i32.get());
  x->AddMember(f32.get());

  s->AddMember(i32.get());
  s->AddMember(x.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(48u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(8U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
  /* a */
  EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  /* b */
  EXPECT_EQ(FormatMode::kSInt, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  /* c */
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[5].IsPadding());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
  EXPECT_EQ(4u, segs[6].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[7].IsPadding());
  EXPECT_EQ(12u, segs[7].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStructNoPack_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto x = MakeUnique<type::Struct>();
  x->AddMember(i32.get());
  x->AddMember(i32.get());
  x->AddMember(f32.get());

  s->AddMember(i32.get());
  s->AddMember(x.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(20u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(5U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* a */
  EXPECT_EQ(FormatMode::kSInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  /* b */
  EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  /* c */
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
}

// struct {
//   int32 w;
//   struct {
//     int32 a;
//     int32 b;
//     float c[3];
//   } x;
//   float y;
// }
TEST_F(FormatTest, SegmentStructWithStructArray_Std140) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto f32_ary = type::Number::Float(32);
  f32_ary->SetIsSizedArray(3);

  auto x = MakeUnique<type::Struct>();
  x->AddMember(i32.get());
  x->AddMember(i32.get());
  x->AddMember(f32_ary.get());

  s->AddMember(i32.get());
  s->AddMember(x.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd140);
  EXPECT_EQ(96u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(13U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[1].IsPadding());
  EXPECT_EQ(12u, segs[1].SizeInBytes());
  /* a */
  EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  /* b */
  EXPECT_EQ(FormatMode::kSInt, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[4].IsPadding());
  EXPECT_EQ(8u, segs[4].SizeInBytes());
  /* c[0] */
  EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[6].IsPadding());
  EXPECT_EQ(12u, segs[6].SizeInBytes());
  /* c[1] */
  EXPECT_EQ(FormatMode::kSFloat, segs[7].GetFormatMode());
  EXPECT_EQ(4u, segs[7].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[8].IsPadding());
  EXPECT_EQ(12u, segs[8].SizeInBytes());
  /* c[2] */
  EXPECT_EQ(FormatMode::kSFloat, segs[9].GetFormatMode());
  EXPECT_EQ(4u, segs[9].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[10].IsPadding());
  EXPECT_EQ(12u, segs[10].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[11].GetFormatMode());
  EXPECT_EQ(4u, segs[11].SizeInBytes());
  /* pad */
  EXPECT_TRUE(segs[12].IsPadding());
  EXPECT_EQ(12u, segs[12].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStructArray_Std430) {
  auto s = MakeUnique<type::Struct>();
  auto f32 = type::Number::Float(32);
  auto i32 = type::Number::Int(32);
  auto f32_ary = type::Number::Float(32);
  f32_ary->SetIsSizedArray(3);
  auto x = MakeUnique<type::Struct>();
  x->AddMember(i32.get());
  x->AddMember(i32.get());
  x->AddMember(f32_ary.get());

  s->AddMember(i32.get());
  s->AddMember(x.get());
  s->AddMember(f32.get());

  Format fmt(s.get());
  fmt.SetLayout(Format::Layout::kStd430);
  EXPECT_EQ(28u, fmt.SizeInBytes());

  const auto& segs = fmt.GetSegments();
  ASSERT_EQ(7U, segs.size());
  /* w */
  EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
  EXPECT_EQ(4u, segs[0].SizeInBytes());
  /* a */
  EXPECT_EQ(FormatMode::kSInt, segs[1].GetFormatMode());
  EXPECT_EQ(4u, segs[1].SizeInBytes());
  /* b */
  EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
  EXPECT_EQ(4u, segs[2].SizeInBytes());
  /* c[0] */
  EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
  EXPECT_EQ(4u, segs[3].SizeInBytes());
  /* c[1] */
  EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
  EXPECT_EQ(4u, segs[4].SizeInBytes());
  /* c[2] */
  EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
  EXPECT_EQ(4u, segs[5].SizeInBytes());
  /* y */
  EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
  EXPECT_EQ(4u, segs[6].SizeInBytes());
}

}  // namespace amber
