// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/cc_messages.h"

#include <string.h>

#include <algorithm>

#include "cc/output/compositor_frame.h"
#include "content/public/common/common_param_traits.h"
#include "ipc/ipc_message.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/effects/SkBlurImageFilter.h"

#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
#endif

using cc::CheckerboardDrawQuad;
using cc::DelegatedFrameData;
using cc::DebugBorderDrawQuad;
using cc::DrawQuad;
using cc::FilterOperation;
using cc::FilterOperations;
using cc::IOSurfaceDrawQuad;
using cc::PictureDrawQuad;
using cc::RenderPass;
using cc::RenderPassId;
using cc::RenderPassDrawQuad;
using cc::ResourceProvider;
using cc::SharedQuadState;
using cc::SoftwareFrameData;
using cc::SolidColorDrawQuad;
using cc::SurfaceDrawQuad;
using cc::TextureDrawQuad;
using cc::TileDrawQuad;
using cc::TransferableResource;
using cc::StreamVideoDrawQuad;
using cc::VideoLayerImpl;
using cc::YUVVideoDrawQuad;
using gfx::Transform;

namespace content {
namespace {

class CCMessagesTest : public testing::Test {
 protected:
  void Compare(const RenderPass* a, const RenderPass* b) {
    EXPECT_EQ(a->id, b->id);
    EXPECT_EQ(a->output_rect.ToString(), b->output_rect.ToString());
    EXPECT_EQ(a->damage_rect.ToString(), b->damage_rect.ToString());
    EXPECT_EQ(a->transform_to_root_target, b->transform_to_root_target);
    EXPECT_EQ(a->has_transparent_background, b->has_transparent_background);
  }

  void Compare(const SharedQuadState* a, const SharedQuadState* b) {
    EXPECT_EQ(a->content_to_target_transform, b->content_to_target_transform);
    EXPECT_EQ(a->content_bounds, b->content_bounds);
    EXPECT_EQ(a->visible_content_rect, b->visible_content_rect);
    EXPECT_EQ(a->clip_rect, b->clip_rect);
    EXPECT_EQ(a->is_clipped, b->is_clipped);
    EXPECT_EQ(a->opacity, b->opacity);
    EXPECT_EQ(a->blend_mode, b->blend_mode);
    EXPECT_EQ(a->sorting_context_id, b->sorting_context_id);
  }

  void Compare(const DrawQuad* a, const DrawQuad* b) {
    ASSERT_NE(DrawQuad::INVALID, a->material);
    ASSERT_EQ(a->material, b->material);
    EXPECT_EQ(a->rect.ToString(), b->rect.ToString());
    EXPECT_EQ(a->opaque_rect.ToString(), b->opaque_rect.ToString());
    EXPECT_EQ(a->visible_rect.ToString(), b->visible_rect.ToString());
    EXPECT_EQ(a->needs_blending, b->needs_blending);

    Compare(a->shared_quad_state, b->shared_quad_state);

    switch (a->material) {
      case DrawQuad::CHECKERBOARD:
        Compare(CheckerboardDrawQuad::MaterialCast(a),
                CheckerboardDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::DEBUG_BORDER:
        Compare(DebugBorderDrawQuad::MaterialCast(a),
                DebugBorderDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::IO_SURFACE_CONTENT:
        Compare(IOSurfaceDrawQuad::MaterialCast(a),
                IOSurfaceDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::PICTURE_CONTENT:
        Compare(PictureDrawQuad::MaterialCast(a),
                PictureDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::RENDER_PASS:
        Compare(RenderPassDrawQuad::MaterialCast(a),
                RenderPassDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::TEXTURE_CONTENT:
        Compare(TextureDrawQuad::MaterialCast(a),
                TextureDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::TILED_CONTENT:
        Compare(TileDrawQuad::MaterialCast(a),
                TileDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::SOLID_COLOR:
        Compare(SolidColorDrawQuad::MaterialCast(a),
                SolidColorDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::STREAM_VIDEO_CONTENT:
        Compare(StreamVideoDrawQuad::MaterialCast(a),
                StreamVideoDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::SURFACE_CONTENT:
        Compare(SurfaceDrawQuad::MaterialCast(a),
                SurfaceDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::YUV_VIDEO_CONTENT:
        Compare(YUVVideoDrawQuad::MaterialCast(a),
                YUVVideoDrawQuad::MaterialCast(b));
        break;
      case DrawQuad::INVALID:
        break;
    }
  }

  void Compare(const CheckerboardDrawQuad* a, const CheckerboardDrawQuad* b) {
    EXPECT_EQ(a->color, b->color);
  }

  void Compare(const DebugBorderDrawQuad* a, const DebugBorderDrawQuad* b) {
    EXPECT_EQ(a->color, b->color);
    EXPECT_EQ(a->width, b->width);
  }

  void Compare(const IOSurfaceDrawQuad* a, const IOSurfaceDrawQuad* b) {
    EXPECT_EQ(a->io_surface_size.ToString(), b->io_surface_size.ToString());
    EXPECT_EQ(a->io_surface_resource_id, b->io_surface_resource_id);
    EXPECT_EQ(a->orientation, b->orientation);
  }

  void Compare(const RenderPassDrawQuad* a, const RenderPassDrawQuad* b) {
    EXPECT_EQ(a->render_pass_id, b->render_pass_id);
    EXPECT_EQ(a->mask_resource_id, b->mask_resource_id);
    EXPECT_EQ(a->mask_uv_rect.ToString(), b->mask_uv_rect.ToString());
    EXPECT_EQ(a->filters.size(), b->filters.size());
    for (size_t i = 0; i < a->filters.size(); ++i) {
      if (a->filters.at(i).type() != cc::FilterOperation::REFERENCE) {
        EXPECT_EQ(a->filters.at(i), b->filters.at(i));
      } else {
        EXPECT_EQ(b->filters.at(i).type(), cc::FilterOperation::REFERENCE);
        EXPECT_EQ(a->filters.at(i).image_filter()->countInputs(),
                  b->filters.at(i).image_filter()->countInputs());
      }
    }
    EXPECT_EQ(a->filters_scale, b->filters_scale);
    EXPECT_EQ(a->background_filters, b->background_filters);
  }

  void Compare(const SolidColorDrawQuad* a, const SolidColorDrawQuad* b) {
    EXPECT_EQ(a->color, b->color);
    EXPECT_EQ(a->force_anti_aliasing_off, b->force_anti_aliasing_off);
  }

  void Compare(const StreamVideoDrawQuad* a, const StreamVideoDrawQuad* b) {
    EXPECT_EQ(a->resource_id, b->resource_id);
    EXPECT_EQ(a->matrix, b->matrix);
  }

  void Compare(const SurfaceDrawQuad* a, const SurfaceDrawQuad* b) {
    EXPECT_EQ(a->surface_id, b->surface_id);
  }

  void Compare(const TextureDrawQuad* a, const TextureDrawQuad* b) {
    EXPECT_EQ(a->resource_id, b->resource_id);
    EXPECT_EQ(a->premultiplied_alpha, b->premultiplied_alpha);
    EXPECT_EQ(a->uv_top_left, b->uv_top_left);
    EXPECT_EQ(a->uv_bottom_right, b->uv_bottom_right);
    EXPECT_EQ(a->background_color, b->background_color);
    EXPECT_EQ(a->vertex_opacity[0], b->vertex_opacity[0]);
    EXPECT_EQ(a->vertex_opacity[1], b->vertex_opacity[1]);
    EXPECT_EQ(a->vertex_opacity[2], b->vertex_opacity[2]);
    EXPECT_EQ(a->vertex_opacity[3], b->vertex_opacity[3]);
    EXPECT_EQ(a->flipped, b->flipped);
  }

  void Compare(const TileDrawQuad* a, const TileDrawQuad* b) {
    EXPECT_EQ(a->resource_id, b->resource_id);
    EXPECT_EQ(a->tex_coord_rect, b->tex_coord_rect);
    EXPECT_EQ(a->texture_size, b->texture_size);
    EXPECT_EQ(a->swizzle_contents, b->swizzle_contents);
  }

  void Compare(const YUVVideoDrawQuad* a, const YUVVideoDrawQuad* b) {
    EXPECT_EQ(a->tex_coord_rect, b->tex_coord_rect);
    EXPECT_EQ(a->y_plane_resource_id, b->y_plane_resource_id);
    EXPECT_EQ(a->u_plane_resource_id, b->u_plane_resource_id);
    EXPECT_EQ(a->v_plane_resource_id, b->v_plane_resource_id);
    EXPECT_EQ(a->a_plane_resource_id, b->a_plane_resource_id);
    EXPECT_EQ(a->color_space, b->color_space);
  }

  void Compare(const TransferableResource& a, const TransferableResource& b) {
    EXPECT_EQ(a.id, b.id);
    EXPECT_EQ(a.format, b.format);
    EXPECT_EQ(a.filter, b.filter);
    EXPECT_EQ(a.size.ToString(), b.size.ToString());
    for (size_t i = 0; i < arraysize(a.mailbox_holder.mailbox.name); ++i) {
      EXPECT_EQ(a.mailbox_holder.mailbox.name[i],
                b.mailbox_holder.mailbox.name[i]);
    }
    EXPECT_EQ(a.mailbox_holder.texture_target, b.mailbox_holder.texture_target);
    EXPECT_EQ(a.mailbox_holder.sync_point, b.mailbox_holder.sync_point);
    EXPECT_EQ(a.allow_overlay, b.allow_overlay);
  }
};

TEST_F(CCMessagesTest, AllQuads) {
  IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);

  Transform arbitrary_matrix;
  arbitrary_matrix.Scale(3, 3);
  arbitrary_matrix.Translate(-5, 20);
  arbitrary_matrix.Rotate(15);
  gfx::Rect arbitrary_rect1(-5, 9, 3, 15);
  gfx::Rect arbitrary_rect1_inside_rect1(-4, 12, 2, 8);
  gfx::Rect arbitrary_rect2_inside_rect1(-5, 11, 1, 2);
  gfx::Rect arbitrary_rect2(40, 23, 11, 7);
  gfx::Rect arbitrary_rect1_inside_rect2(44, 23, 4, 2);
  gfx::Rect arbitrary_rect2_inside_rect2(41, 25, 3, 5);
  gfx::Rect arbitrary_rect3(7, -53, 22, 19);
  gfx::Rect arbitrary_rect1_inside_rect3(10, -40, 6, 3);
  gfx::Rect arbitrary_rect2_inside_rect3(12, -51, 5, 12);
  gfx::Size arbitrary_size1(15, 19);
  gfx::Size arbitrary_size2(3, 99);
  gfx::Size arbitrary_size3(75, 1281);
  gfx::RectF arbitrary_rectf1(4.2f, -922.1f, 15.6f, 29.5f);
  gfx::SizeF arbitrary_sizef1(15.2f, 104.6f);
  gfx::PointF arbitrary_pointf1(31.4f, 15.9f);
  gfx::PointF arbitrary_pointf2(26.5f, -35.8f);
  gfx::Vector2dF arbitrary_vector2df1(16.2f, -85.1f);
  float arbitrary_float1 = 0.7f;
  float arbitrary_float2 = 0.3f;
  float arbitrary_float3 = 0.9f;
  float arbitrary_float_array[4] = {3.5f, 6.2f, 9.3f, 12.3f};
  bool arbitrary_bool1 = true;
  bool arbitrary_bool2 = false;
  bool arbitrary_bool3 = true;
  int arbitrary_context_id1 = 12;
  int arbitrary_context_id2 = 57;
  int arbitrary_context_id3 = -503;
  int arbitrary_int = 5;
  SkColor arbitrary_color = SkColorSetARGB(25, 36, 47, 58);
  SkXfermode::Mode arbitrary_blend_mode1 = SkXfermode::kScreen_Mode;
  SkXfermode::Mode arbitrary_blend_mode2 = SkXfermode::kLighten_Mode;
  SkXfermode::Mode arbitrary_blend_mode3 = SkXfermode::kOverlay_Mode;
  IOSurfaceDrawQuad::Orientation arbitrary_orientation =
      IOSurfaceDrawQuad::UNFLIPPED;
  RenderPassId arbitrary_id(10, 14);
  ResourceProvider::ResourceId arbitrary_resourceid1 = 55;
  ResourceProvider::ResourceId arbitrary_resourceid2 = 47;
  ResourceProvider::ResourceId arbitrary_resourceid3 = 23;
  ResourceProvider::ResourceId arbitrary_resourceid4 = 16;
  SkScalar arbitrary_sigma = SkFloatToScalar(2.0f);
  YUVVideoDrawQuad::ColorSpace arbitrary_color_space =
      YUVVideoDrawQuad::REC_601;

  FilterOperations arbitrary_filters1;
  arbitrary_filters1.Append(FilterOperation::CreateGrayscaleFilter(
      arbitrary_float1));
  skia::RefPtr<SkImageFilter> arbitrary_filter = skia::AdoptRef(
      SkBlurImageFilter::Create(arbitrary_sigma, arbitrary_sigma));
  arbitrary_filters1.Append(
      cc::FilterOperation::CreateReferenceFilter(arbitrary_filter));

  FilterOperations arbitrary_filters2;
  arbitrary_filters2.Append(FilterOperation::CreateBrightnessFilter(
      arbitrary_float2));

  scoped_ptr<RenderPass> pass_in = RenderPass::Create();
  pass_in->SetAll(arbitrary_id,
                  arbitrary_rect1,
                  arbitrary_rect2,
                  arbitrary_matrix,
                  arbitrary_bool1);

  SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state1_in->SetAll(arbitrary_matrix,
                           arbitrary_size1,
                           arbitrary_rect1,
                           arbitrary_rect2,
                           arbitrary_bool1,
                           arbitrary_float1,
                           arbitrary_blend_mode1,
                           arbitrary_context_id1);

  scoped_ptr<RenderPass> pass_cmp = RenderPass::Create();
  pass_cmp->SetAll(arbitrary_id,
                   arbitrary_rect1,
                   arbitrary_rect2,
                   arbitrary_matrix,
                   arbitrary_bool1);

  SharedQuadState* shared_state1_cmp =
      pass_cmp->CreateAndAppendSharedQuadState();
  shared_state1_cmp->CopyFrom(shared_state1_in);

  CheckerboardDrawQuad* checkerboard_in =
      pass_in->CreateAndAppendDrawQuad<CheckerboardDrawQuad>();
  checkerboard_in->SetAll(shared_state1_in,
                          arbitrary_rect1,
                          arbitrary_rect2_inside_rect1,
                          arbitrary_rect1_inside_rect1,
                          arbitrary_bool1,
                          arbitrary_color);
  pass_cmp->CopyFromAndAppendDrawQuad(checkerboard_in,
                                      checkerboard_in->shared_quad_state);

  DebugBorderDrawQuad* debugborder_in =
      pass_in->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
  debugborder_in->SetAll(shared_state1_in,
                         arbitrary_rect3,
                         arbitrary_rect1_inside_rect3,
                         arbitrary_rect2_inside_rect3,
                         arbitrary_bool1,
                         arbitrary_color,
                         arbitrary_int);
  pass_cmp->CopyFromAndAppendDrawQuad(debugborder_in,
                                      debugborder_in->shared_quad_state);

  IOSurfaceDrawQuad* iosurface_in =
      pass_in->CreateAndAppendDrawQuad<IOSurfaceDrawQuad>();
  iosurface_in->SetAll(shared_state1_in,
                       arbitrary_rect2,
                       arbitrary_rect2_inside_rect2,
                       arbitrary_rect1_inside_rect2,
                       arbitrary_bool1,
                       arbitrary_size1,
                       arbitrary_resourceid3,
                       arbitrary_orientation);
  pass_cmp->CopyFromAndAppendDrawQuad(iosurface_in,
                                      iosurface_in->shared_quad_state);

  SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state2_in->SetAll(arbitrary_matrix,
                           arbitrary_size2,
                           arbitrary_rect2,
                           arbitrary_rect3,
                           arbitrary_bool1,
                           arbitrary_float2,
                           arbitrary_blend_mode2,
                           arbitrary_context_id2);
  SharedQuadState* shared_state2_cmp =
      pass_cmp->CreateAndAppendSharedQuadState();
  shared_state2_cmp->CopyFrom(shared_state2_in);

  RenderPassDrawQuad* renderpass_in =
      pass_in->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
  renderpass_in->SetAll(shared_state2_in,
                        arbitrary_rect1,
                        arbitrary_rect2_inside_rect1,
                        arbitrary_rect1_inside_rect1,
                        arbitrary_bool1,
                        arbitrary_id,
                        arbitrary_resourceid2,
                        arbitrary_rectf1,
                        arbitrary_filters1,
                        arbitrary_vector2df1,
                        arbitrary_filters2);
  pass_cmp->CopyFromAndAppendRenderPassDrawQuad(
      renderpass_in,
      renderpass_in->shared_quad_state,
      renderpass_in->render_pass_id);

  SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state3_in->SetAll(arbitrary_matrix,
                           arbitrary_size3,
                           arbitrary_rect3,
                           arbitrary_rect1,
                           arbitrary_bool1,
                           arbitrary_float3,
                           arbitrary_blend_mode3,
                           arbitrary_context_id3);
  SharedQuadState* shared_state3_cmp =
      pass_cmp->CreateAndAppendSharedQuadState();
  shared_state3_cmp->CopyFrom(shared_state3_in);

  SolidColorDrawQuad* solidcolor_in =
      pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
  solidcolor_in->SetAll(shared_state3_in,
                        arbitrary_rect3,
                        arbitrary_rect1_inside_rect3,
                        arbitrary_rect2_inside_rect3,
                        arbitrary_bool1,
                        arbitrary_color,
                        arbitrary_bool2);
  pass_cmp->CopyFromAndAppendDrawQuad(solidcolor_in,
                                      solidcolor_in->shared_quad_state);

  StreamVideoDrawQuad* streamvideo_in =
      pass_in->CreateAndAppendDrawQuad<StreamVideoDrawQuad>();
  streamvideo_in->SetAll(shared_state3_in,
                         arbitrary_rect2,
                         arbitrary_rect2_inside_rect2,
                         arbitrary_rect1_inside_rect2,
                         arbitrary_bool1,
                         arbitrary_resourceid2,
                         arbitrary_matrix);
  pass_cmp->CopyFromAndAppendDrawQuad(streamvideo_in,
                                      streamvideo_in->shared_quad_state);

  cc::SurfaceId arbitrary_surface_id(3);
  SurfaceDrawQuad* surface_in =
      pass_in->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
  surface_in->SetAll(shared_state3_in,
                     arbitrary_rect2,
                     arbitrary_rect2_inside_rect2,
                     arbitrary_rect1_inside_rect2,
                     arbitrary_bool1,
                     arbitrary_surface_id);
  pass_cmp->CopyFromAndAppendDrawQuad(surface_in,
                                      surface_in->shared_quad_state);

  TextureDrawQuad* texture_in =
      pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>();
  texture_in->SetAll(shared_state3_in,
                     arbitrary_rect2,
                     arbitrary_rect2_inside_rect2,
                     arbitrary_rect1_inside_rect2,
                     arbitrary_bool1,
                     arbitrary_resourceid1,
                     arbitrary_bool2,
                     arbitrary_pointf1,
                     arbitrary_pointf2,
                     arbitrary_color,
                     arbitrary_float_array,
                     arbitrary_bool3);
  pass_cmp->CopyFromAndAppendDrawQuad(texture_in,
                                      texture_in->shared_quad_state);

  TileDrawQuad* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>();
  tile_in->SetAll(shared_state3_in,
                  arbitrary_rect2,
                  arbitrary_rect2_inside_rect2,
                  arbitrary_rect1_inside_rect2,
                  arbitrary_bool1,
                  arbitrary_resourceid3,
                  arbitrary_rectf1,
                  arbitrary_size1,
                  arbitrary_bool2);
  pass_cmp->CopyFromAndAppendDrawQuad(tile_in, tile_in->shared_quad_state);

  YUVVideoDrawQuad* yuvvideo_in =
      pass_in->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
  yuvvideo_in->SetAll(shared_state3_in,
                      arbitrary_rect1,
                      arbitrary_rect2_inside_rect1,
                      arbitrary_rect1_inside_rect1,
                      arbitrary_bool1,
                      arbitrary_rectf1,
                      arbitrary_resourceid1,
                      arbitrary_resourceid2,
                      arbitrary_resourceid3,
                      arbitrary_resourceid4,
                      arbitrary_color_space);
  pass_cmp->CopyFromAndAppendDrawQuad(yuvvideo_in,
                                      yuvvideo_in->shared_quad_state);

  // Make sure the in and cmp RenderPasses match.
  Compare(pass_cmp.get(), pass_in.get());
  ASSERT_EQ(3u, pass_in->shared_quad_state_list.size());
  ASSERT_EQ(10u, pass_in->quad_list.size());
  for (size_t i = 0; i < 3; ++i) {
    Compare(pass_cmp->shared_quad_state_list[i],
            pass_in->shared_quad_state_list[i]);
  }
  for (cc::QuadList::Iterator in_iter = pass_in->quad_list.begin(),
                              cmp_iter = pass_cmp->quad_list.begin();
       in_iter != pass_in->quad_list.end();
       ++in_iter, ++cmp_iter)
    Compare(&*cmp_iter, &*in_iter);

  for (size_t i = 1; i < pass_in->quad_list.size(); ++i) {
    bool same_shared_quad_state_cmp =
        pass_cmp->quad_list.ElementAt(i)->shared_quad_state ==
        pass_cmp->quad_list.ElementAt(i - 1)->shared_quad_state;
    bool same_shared_quad_state_in =
        pass_in->quad_list.ElementAt(i)->shared_quad_state ==
        pass_in->quad_list.ElementAt(i - 1)->shared_quad_state;
    EXPECT_EQ(same_shared_quad_state_cmp, same_shared_quad_state_in);
  }

  DelegatedFrameData frame_in;
  frame_in.render_pass_list.push_back(pass_in.Pass());

  IPC::ParamTraits<DelegatedFrameData>::Write(&msg, frame_in);

  DelegatedFrameData frame_out;
  PickleIterator iter(msg);
  EXPECT_TRUE(IPC::ParamTraits<DelegatedFrameData>::Read(&msg,
      &iter, &frame_out));

  // Make sure the out and cmp RenderPasses match.
  scoped_ptr<RenderPass> pass_out = frame_out.render_pass_list.take(
      frame_out.render_pass_list.begin());
  Compare(pass_cmp.get(), pass_out.get());
  ASSERT_EQ(3u, pass_out->shared_quad_state_list.size());
  ASSERT_EQ(10u, pass_out->quad_list.size());
  for (size_t i = 0; i < 3; ++i) {
    Compare(pass_cmp->shared_quad_state_list[i],
            pass_out->shared_quad_state_list[i]);
  }
  for (cc::QuadList::Iterator out_iter = pass_out->quad_list.begin(),
                              cmp_iter = pass_cmp->quad_list.begin();
       out_iter != pass_out->quad_list.end();
       ++out_iter, ++cmp_iter)
    Compare(&*cmp_iter, &*out_iter);

  for (size_t i = 1; i < pass_out->quad_list.size(); ++i) {
    bool same_shared_quad_state_cmp =
        pass_cmp->quad_list.ElementAt(i)->shared_quad_state ==
        pass_cmp->quad_list.ElementAt(i - 1)->shared_quad_state;
    bool same_shared_quad_state_out =
        pass_out->quad_list.ElementAt(i)->shared_quad_state ==
        pass_out->quad_list.ElementAt(i - 1)->shared_quad_state;
    EXPECT_EQ(same_shared_quad_state_cmp, same_shared_quad_state_out);
  }
}

TEST_F(CCMessagesTest, UnusedSharedQuadStates) {
  scoped_ptr<RenderPass> pass_in = RenderPass::Create();
  pass_in->SetAll(RenderPassId(1, 1),
                  gfx::Rect(100, 100),
                  gfx::Rect(),
                  gfx::Transform(),
                  false);

  // The first SharedQuadState is used.
  SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state1_in->SetAll(gfx::Transform(),
                           gfx::Size(1, 1),
                           gfx::Rect(),
                           gfx::Rect(),
                           false,
                           1.f,
                           SkXfermode::kSrcOver_Mode,
                           0);

  CheckerboardDrawQuad* quad1 =
      pass_in->CreateAndAppendDrawQuad<CheckerboardDrawQuad>();
  quad1->SetAll(shared_state1_in,
                gfx::Rect(10, 10),
                gfx::Rect(10, 10),
                gfx::Rect(10, 10),
                false,
                SK_ColorRED);

  // The second and third SharedQuadStates are not used.
  SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state2_in->SetAll(gfx::Transform(),
                           gfx::Size(2, 2),
                           gfx::Rect(),
                           gfx::Rect(),
                           false,
                           1.f,
                           SkXfermode::kSrcOver_Mode,
                           0);

  SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state3_in->SetAll(gfx::Transform(),
                           gfx::Size(3, 3),
                           gfx::Rect(),
                           gfx::Rect(),
                           false,
                           1.f,
                           SkXfermode::kSrcOver_Mode,
                           0);

  // The fourth SharedQuadState is used.
  SharedQuadState* shared_state4_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state4_in->SetAll(gfx::Transform(),
                           gfx::Size(4, 4),
                           gfx::Rect(),
                           gfx::Rect(),
                           false,
                           1.f,
                           SkXfermode::kSrcOver_Mode,
                           0);

  CheckerboardDrawQuad* quad2 =
      pass_in->CreateAndAppendDrawQuad<CheckerboardDrawQuad>();
  quad2->SetAll(shared_state4_in,
                gfx::Rect(10, 10),
                gfx::Rect(10, 10),
                gfx::Rect(10, 10),
                false,
                SK_ColorRED);

  // The fifth is not used again.
  SharedQuadState* shared_state5_in = pass_in->CreateAndAppendSharedQuadState();
  shared_state5_in->SetAll(gfx::Transform(),
                           gfx::Size(5, 5),
                           gfx::Rect(),
                           gfx::Rect(),
                           false,
                           1.f,
                           SkXfermode::kSrcOver_Mode,
                           0);

  // 5 SharedQuadStates go in.
  ASSERT_EQ(5u, pass_in->shared_quad_state_list.size());
  ASSERT_EQ(2u, pass_in->quad_list.size());

  DelegatedFrameData frame_in;
  frame_in.render_pass_list.push_back(pass_in.Pass());

  IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
  IPC::ParamTraits<DelegatedFrameData>::Write(&msg, frame_in);

  DelegatedFrameData frame_out;
  PickleIterator iter(msg);
  EXPECT_TRUE(
      IPC::ParamTraits<DelegatedFrameData>::Read(&msg, &iter, &frame_out));

  scoped_ptr<RenderPass> pass_out =
      frame_out.render_pass_list.take(frame_out.render_pass_list.begin());

  // 2 SharedQuadStates come out. The first and fourth SharedQuadStates were
  // used by quads, and so serialized. Others were not.
  ASSERT_EQ(2u, pass_out->shared_quad_state_list.size());
  ASSERT_EQ(2u, pass_out->quad_list.size());

  EXPECT_EQ(gfx::Size(1, 1).ToString(),
            pass_out->shared_quad_state_list[0]->content_bounds.ToString());
  EXPECT_EQ(gfx::Size(4, 4).ToString(),
            pass_out->shared_quad_state_list[1]->content_bounds.ToString());
}

TEST_F(CCMessagesTest, Resources) {
  IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
  gfx::Size arbitrary_size(757, 1281);
  unsigned int arbitrary_uint1 = 71234838;
  unsigned int arbitrary_uint2 = 53589793;

  GLbyte arbitrary_mailbox1[GL_MAILBOX_SIZE_CHROMIUM] = {
      1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2,
      3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4,
      5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4};

  GLbyte arbitrary_mailbox2[GL_MAILBOX_SIZE_CHROMIUM] = {
      0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 9, 7, 5, 3, 1, 2, 4, 6, 8, 0, 0, 9,
      8, 7, 6, 5, 4, 3, 2, 1, 9, 7, 5, 3, 1, 2, 4, 6, 8, 0, 0, 9, 8, 7,
      6, 5, 4, 3, 2, 1, 9, 7, 5, 3, 1, 2, 4, 6, 8, 0, 0, 9, 8, 7};

  TransferableResource arbitrary_resource1;
  arbitrary_resource1.id = 2178312;
  arbitrary_resource1.format = cc::RGBA_8888;
  arbitrary_resource1.filter = 53;
  arbitrary_resource1.size = gfx::Size(37189, 123123);
  arbitrary_resource1.mailbox_holder.mailbox.SetName(arbitrary_mailbox1);
  arbitrary_resource1.mailbox_holder.texture_target = GL_TEXTURE_2D;
  arbitrary_resource1.mailbox_holder.sync_point = arbitrary_uint1;
  arbitrary_resource1.allow_overlay = true;

  TransferableResource arbitrary_resource2;
  arbitrary_resource2.id = 789132;
  arbitrary_resource2.format = cc::RGBA_4444;
  arbitrary_resource2.filter = 47;
  arbitrary_resource2.size = gfx::Size(89123, 23789);
  arbitrary_resource2.mailbox_holder.mailbox.SetName(arbitrary_mailbox2);
  arbitrary_resource2.mailbox_holder.texture_target = GL_TEXTURE_EXTERNAL_OES;
  arbitrary_resource2.mailbox_holder.sync_point = arbitrary_uint2;
  arbitrary_resource2.allow_overlay = false;

  scoped_ptr<RenderPass> renderpass_in = RenderPass::Create();
  renderpass_in->SetNew(
      RenderPassId(1, 1), gfx::Rect(), gfx::Rect(), gfx::Transform());

  DelegatedFrameData frame_in;
  frame_in.resource_list.push_back(arbitrary_resource1);
  frame_in.resource_list.push_back(arbitrary_resource2);
  frame_in.render_pass_list.push_back(renderpass_in.Pass());

  IPC::ParamTraits<DelegatedFrameData>::Write(&msg, frame_in);

  DelegatedFrameData frame_out;
  PickleIterator iter(msg);
  EXPECT_TRUE(IPC::ParamTraits<DelegatedFrameData>::Read(&msg,
      &iter, &frame_out));

  ASSERT_EQ(2u, frame_out.resource_list.size());
  Compare(arbitrary_resource1, frame_out.resource_list[0]);
  Compare(arbitrary_resource2, frame_out.resource_list[1]);
}

TEST_F(CCMessagesTest, SoftwareFrameData) {
  cc::SoftwareFrameData frame_in;
  frame_in.id = 3;
  frame_in.size = gfx::Size(40, 20);
  frame_in.damage_rect = gfx::Rect(5, 18, 31, 44);
  frame_in.bitmap_id = cc::SharedBitmap::GenerateId();

  // Write the frame.
  IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
  IPC::ParamTraits<cc::SoftwareFrameData>::Write(&msg, frame_in);

  // Read the frame.
  cc::SoftwareFrameData frame_out;
  PickleIterator iter(msg);
  EXPECT_TRUE(
      IPC::ParamTraits<SoftwareFrameData>::Read(&msg, &iter, &frame_out));
  EXPECT_EQ(frame_in.id, frame_out.id);
  EXPECT_EQ(frame_in.size.ToString(), frame_out.size.ToString());
  EXPECT_EQ(frame_in.damage_rect.ToString(), frame_out.damage_rect.ToString());
  EXPECT_EQ(frame_in.bitmap_id, frame_out.bitmap_id);
}

TEST_F(CCMessagesTest, SoftwareFrameDataMaxInt) {
  SoftwareFrameData frame_in;
  frame_in.id = 3;
  frame_in.size = gfx::Size(40, 20);
  frame_in.damage_rect = gfx::Rect(5, 18, 31, 44);
  frame_in.bitmap_id = cc::SharedBitmap::GenerateId();

  // Write the SoftwareFrameData by hand, make sure it works.
  {
    IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
    IPC::WriteParam(&msg, frame_in.id);
    IPC::WriteParam(&msg, frame_in.size);
    IPC::WriteParam(&msg, frame_in.damage_rect);
    IPC::WriteParam(&msg, frame_in.bitmap_id);
    SoftwareFrameData frame_out;
    PickleIterator iter(msg);
    EXPECT_TRUE(
        IPC::ParamTraits<SoftwareFrameData>::Read(&msg, &iter, &frame_out));
  }

  // The size of the frame may overflow when multiplied together.
  int max = std::numeric_limits<int>::max();
  frame_in.size = gfx::Size(max, max);

  // If size_t is larger than int, then int*int*4 can always fit in size_t.
  bool expect_read = sizeof(size_t) >= sizeof(int) * 2;

  // Write the SoftwareFrameData with the MaxInt size, if it causes overflow it
  // should fail.
  {
    IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
    IPC::WriteParam(&msg, frame_in.id);
    IPC::WriteParam(&msg, frame_in.size);
    IPC::WriteParam(&msg, frame_in.damage_rect);
    IPC::WriteParam(&msg, frame_in.bitmap_id);
    SoftwareFrameData frame_out;
    PickleIterator iter(msg);
    EXPECT_EQ(
        expect_read,
        IPC::ParamTraits<SoftwareFrameData>::Read(&msg, &iter, &frame_out));
  }
}

}  // namespace
}  // namespace content