// Copyright (c) 2009 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 "flip_protocol.h"

#include "base/scoped_ptr.h"
#include "net/flip/flip_bitmasks.h"
#include "net/flip/flip_framer.h"
#include "testing/platform_test.h"

using flip::FlipDataFrame;
using flip::FlipFrame;
using flip::FlipControlFrame;
using flip::FlipSynStreamControlFrame;
using flip::FlipSynReplyControlFrame;
using flip::FlipFinStreamControlFrame;
using flip::FlipFramer;
using flip::FlipHeaderBlock;
using flip::FlagsAndLength;
using flip::kLengthMask;
using flip::kStreamIdMask;
using flip::kFlipProtocolVersion;
using flip::SYN_STREAM;
using flip::SYN_REPLY;
using flip::FIN_STREAM;
using flip::CONTROL_FLAG_FIN;
using flip::CONTROL_FLAG_NONE;

namespace {

// Test our protocol constants
TEST(FlipProtocolTest, ProtocolConstants) {
  EXPECT_EQ(8u, FlipFrame::size());
  EXPECT_EQ(8u, FlipDataFrame::size());
  EXPECT_EQ(12u, FlipControlFrame::size());
  EXPECT_EQ(14u, FlipSynStreamControlFrame::size());
  EXPECT_EQ(14u, FlipSynReplyControlFrame::size());
  EXPECT_EQ(16u, FlipFinStreamControlFrame::size());
  EXPECT_EQ(4u, sizeof(FlagsAndLength));
  EXPECT_EQ(1, SYN_STREAM);
  EXPECT_EQ(2, SYN_REPLY);
  EXPECT_EQ(3, FIN_STREAM);
}

// Test some of the protocol helper functions
TEST(FlipProtocolTest, FrameStructs) {
  FlipFrame frame(FlipFrame::size());
  frame.set_length(12345);
  frame.set_flags(10);
  EXPECT_EQ(12345u, frame.length());
  EXPECT_EQ(10u, frame.flags());
  EXPECT_EQ(false, frame.is_control_frame());

  frame.set_length(0);
  frame.set_flags(10);
  EXPECT_EQ(0u, frame.length());
  EXPECT_EQ(10u, frame.flags());
  EXPECT_EQ(false, frame.is_control_frame());
}

TEST(FlipProtocolTest, DataFrameStructs) {
  FlipDataFrame data_frame;
  data_frame.set_stream_id(12345);
  EXPECT_EQ(12345u, data_frame.stream_id());
}

TEST(FlipProtocolTest, ControlFrameStructs) {
  FlipFramer framer;
  FlipHeaderBlock headers;

  scoped_ptr<FlipSynStreamControlFrame> syn_frame(
      framer.CreateSynStream(123, 2, CONTROL_FLAG_FIN, false, &headers));
  EXPECT_EQ(kFlipProtocolVersion, syn_frame->version());
  EXPECT_EQ(true, syn_frame->is_control_frame());
  EXPECT_EQ(SYN_STREAM, syn_frame->type());
  EXPECT_EQ(123u, syn_frame->stream_id());
  EXPECT_EQ(2u, syn_frame->priority());
  EXPECT_EQ(2, syn_frame->header_block_len());
  EXPECT_EQ(1u, syn_frame->flags());

  scoped_ptr<FlipSynReplyControlFrame> syn_reply(
      framer.CreateSynReply(123, CONTROL_FLAG_NONE, false, &headers));
  EXPECT_EQ(kFlipProtocolVersion, syn_reply->version());
  EXPECT_EQ(true, syn_reply->is_control_frame());
  EXPECT_EQ(SYN_REPLY, syn_reply->type());
  EXPECT_EQ(123u, syn_reply->stream_id());
  EXPECT_EQ(2, syn_reply->header_block_len());
  EXPECT_EQ(0, syn_reply->flags());

  scoped_ptr<FlipFinStreamControlFrame> fin_frame(
      framer.CreateFinStream(123, 444));
  EXPECT_EQ(kFlipProtocolVersion, fin_frame->version());
  EXPECT_EQ(true, fin_frame->is_control_frame());
  EXPECT_EQ(FIN_STREAM, fin_frame->type());
  EXPECT_EQ(123u, fin_frame->stream_id());
  EXPECT_EQ(444u, fin_frame->status());
  fin_frame->set_status(555);
  EXPECT_EQ(555u, fin_frame->status());
  EXPECT_EQ(0, fin_frame->flags());
}

TEST(FlipProtocolTest, TestDataFrame) {
  FlipDataFrame frame;

  // Set the stream ID to various values.
  frame.set_stream_id(0);
  EXPECT_EQ(0u, frame.stream_id());
  EXPECT_FALSE(frame.is_control_frame());
  frame.set_stream_id(~0 & kStreamIdMask);
  EXPECT_EQ(~0 & kStreamIdMask, frame.stream_id());
  EXPECT_FALSE(frame.is_control_frame());

  // Set length to various values.  Make sure that when you set_length(x),
  // length() == x.  Also make sure the flags are unaltered.
  memset(frame.data(), '1', FlipDataFrame::size());
  int8 flags = frame.flags();
  frame.set_length(0);
  EXPECT_EQ(0u, frame.length());
  EXPECT_EQ(flags, frame.flags());
  frame.set_length(kLengthMask);
  EXPECT_EQ(kLengthMask, frame.length());
  EXPECT_EQ(flags, frame.flags());
  frame.set_length(5u);
  EXPECT_EQ(5u, frame.length());
  EXPECT_EQ(flags, frame.flags());

  // Set flags to various values.  Make sure that when you set_flags(x),
  // flags() == x.  Also make sure the length is unaltered.
  memset(frame.data(), '1', FlipDataFrame::size());
  uint32 length = frame.length();
  frame.set_flags(0);
  EXPECT_EQ(0u, frame.flags());
  EXPECT_EQ(length, frame.length());
  int8 all_flags = ~0;
  frame.set_flags(all_flags);
  flags = frame.flags();
  EXPECT_EQ(all_flags, flags);
  EXPECT_EQ(length, frame.length());
  frame.set_flags(5u);
  EXPECT_EQ(5u, frame.flags());
  EXPECT_EQ(length, frame.length());
}

// Make sure that overflows both die in debug mode, and do not cause problems
// in opt mode.  Note:  Chrome doesn't die on DCHECK failures, so the
// EXPECT_DEBUG_DEATH doesn't work.
TEST(FlipProtocolDeathTest, TestDataFrame) {
  FlipDataFrame frame;

  frame.set_stream_id(0);
#ifndef WIN32
  EXPECT_DEBUG_DEATH(frame.set_stream_id(~0), "");
#endif
  EXPECT_FALSE(frame.is_control_frame());

  frame.set_flags(0);
#ifndef WIN32
  EXPECT_DEBUG_DEATH(frame.set_length(~0), "");
#endif
  EXPECT_EQ(0, frame.flags());
}

TEST(FlipProtocolDeathTest, TestFlipControlFrame) {
  FlipControlFrame frame(FlipControlFrame::size());
  memset(frame.data(), '1', FlipControlFrame::size());

  // Set the stream ID to various values.
  frame.set_stream_id(0);
  EXPECT_EQ(0u, frame.stream_id());
  EXPECT_FALSE(frame.is_control_frame());
  frame.set_stream_id(~0 & kStreamIdMask);
  EXPECT_EQ(~0 & kStreamIdMask, frame.stream_id());
  EXPECT_FALSE(frame.is_control_frame());
}

}  // namespace

