root/net/spdy/spdy_write_queue_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. StringToProducer
  2. IntToProducer
  3. ProducerToString
  4. ProducerToInt
  5. MakeTestStream
  6. TEST_F
  7. TEST_F
  8. TEST_F
  9. TEST_F
  10. TEST_F

// Copyright (c) 2013 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 "net/spdy/spdy_write_queue.h"

#include <cstddef>
#include <cstring>
#include <string>

#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "net/base/net_log.h"
#include "net/base/request_priority.h"
#include "net/spdy/spdy_buffer_producer.h"
#include "net/spdy/spdy_stream.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace net {

namespace {

class SpdyWriteQueueTest : public ::testing::Test {};

// Makes a SpdyFrameProducer producing a frame with the data in the
// given string.
scoped_ptr<SpdyBufferProducer> StringToProducer(const std::string& s) {
  scoped_ptr<char[]> data(new char[s.size()]);
  std::memcpy(data.get(), s.data(), s.size());
  return scoped_ptr<SpdyBufferProducer>(
      new SimpleBufferProducer(
          scoped_ptr<SpdyBuffer>(
              new SpdyBuffer(
                  scoped_ptr<SpdyFrame>(
                      new SpdyFrame(data.release(), s.size(), true))))));
}

// Makes a SpdyBufferProducer producing a frame with the data in the
// given int (converted to a string).
scoped_ptr<SpdyBufferProducer> IntToProducer(int i) {
  return StringToProducer(base::IntToString(i));
}

// Produces a frame with the given producer and returns a copy of its
// data as a string.
std::string ProducerToString(scoped_ptr<SpdyBufferProducer> producer) {
  scoped_ptr<SpdyBuffer> buffer = producer->ProduceBuffer();
  return std::string(buffer->GetRemainingData(), buffer->GetRemainingSize());
}

// Produces a frame with the given producer and returns a copy of its
// data as an int (converted from a string).
int ProducerToInt(scoped_ptr<SpdyBufferProducer> producer) {
  int i = 0;
  EXPECT_TRUE(base::StringToInt(ProducerToString(producer.Pass()), &i));
  return i;
}

// Makes a SpdyStream with the given priority and a NULL SpdySession
// -- be careful to not call any functions that expect the session to
// be there.
SpdyStream* MakeTestStream(RequestPriority priority) {
  return new SpdyStream(
      SPDY_BIDIRECTIONAL_STREAM, base::WeakPtr<SpdySession>(),
      GURL(), priority, 0, 0, BoundNetLog());
}

// Add some frame producers of different priority. The producers
// should be dequeued in priority order with their associated stream.
TEST_F(SpdyWriteQueueTest, DequeuesByPriority) {
  SpdyWriteQueue write_queue;

  scoped_ptr<SpdyBufferProducer> producer_low = StringToProducer("LOW");
  scoped_ptr<SpdyBufferProducer> producer_medium = StringToProducer("MEDIUM");
  scoped_ptr<SpdyBufferProducer> producer_highest = StringToProducer("HIGHEST");

  scoped_ptr<SpdyStream> stream_medium(MakeTestStream(MEDIUM));
  scoped_ptr<SpdyStream> stream_highest(MakeTestStream(HIGHEST));

  // A NULL stream should still work.
  write_queue.Enqueue(
      LOW, SYN_STREAM, producer_low.Pass(), base::WeakPtr<SpdyStream>());
  write_queue.Enqueue(
      MEDIUM, SYN_REPLY, producer_medium.Pass(), stream_medium->GetWeakPtr());
  write_queue.Enqueue(
      HIGHEST, RST_STREAM, producer_highest.Pass(),
      stream_highest->GetWeakPtr());

  SpdyFrameType frame_type = DATA;
  scoped_ptr<SpdyBufferProducer> frame_producer;
  base::WeakPtr<SpdyStream> stream;
  ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
  EXPECT_EQ(RST_STREAM, frame_type);
  EXPECT_EQ("HIGHEST", ProducerToString(frame_producer.Pass()));
  EXPECT_EQ(stream_highest, stream.get());

  ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
  EXPECT_EQ(SYN_REPLY, frame_type);
  EXPECT_EQ("MEDIUM", ProducerToString(frame_producer.Pass()));
  EXPECT_EQ(stream_medium, stream.get());

  ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
  EXPECT_EQ(SYN_STREAM, frame_type);
  EXPECT_EQ("LOW", ProducerToString(frame_producer.Pass()));
  EXPECT_EQ(NULL, stream.get());

  EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
}

// Add some frame producers with the same priority. The producers
// should be dequeued in FIFO order with their associated stream.
TEST_F(SpdyWriteQueueTest, DequeuesFIFO) {
  SpdyWriteQueue write_queue;

  scoped_ptr<SpdyBufferProducer> producer1 = IntToProducer(1);
  scoped_ptr<SpdyBufferProducer> producer2 = IntToProducer(2);
  scoped_ptr<SpdyBufferProducer> producer3 = IntToProducer(3);

  scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY));
  scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY));
  scoped_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY));

  write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, producer1.Pass(),
                      stream1->GetWeakPtr());
  write_queue.Enqueue(DEFAULT_PRIORITY, SYN_REPLY, producer2.Pass(),
                      stream2->GetWeakPtr());
  write_queue.Enqueue(DEFAULT_PRIORITY, RST_STREAM, producer3.Pass(),
                      stream3->GetWeakPtr());

  SpdyFrameType frame_type = DATA;
  scoped_ptr<SpdyBufferProducer> frame_producer;
  base::WeakPtr<SpdyStream> stream;
  ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
  EXPECT_EQ(SYN_STREAM, frame_type);
  EXPECT_EQ(1, ProducerToInt(frame_producer.Pass()));
  EXPECT_EQ(stream1, stream.get());

  ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
  EXPECT_EQ(SYN_REPLY, frame_type);
  EXPECT_EQ(2, ProducerToInt(frame_producer.Pass()));
  EXPECT_EQ(stream2, stream.get());

  ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
  EXPECT_EQ(RST_STREAM, frame_type);
  EXPECT_EQ(3, ProducerToInt(frame_producer.Pass()));
  EXPECT_EQ(stream3, stream.get());

  EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
}

// Enqueue a bunch of writes and then call
// RemovePendingWritesForStream() on one of the streams. No dequeued
// write should be for that stream.
TEST_F(SpdyWriteQueueTest, RemovePendingWritesForStream) {
  SpdyWriteQueue write_queue;

  scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY));
  scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY));

  for (int i = 0; i < 100; ++i) {
    base::WeakPtr<SpdyStream> stream =
        (((i % 3) == 0) ? stream1 : stream2)->GetWeakPtr();
    write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i), stream);
  }

  write_queue.RemovePendingWritesForStream(stream2->GetWeakPtr());

  for (int i = 0; i < 100; i += 3) {
    SpdyFrameType frame_type = DATA;
    scoped_ptr<SpdyBufferProducer> frame_producer;
    base::WeakPtr<SpdyStream> stream;
    ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
    EXPECT_EQ(SYN_STREAM, frame_type);
    EXPECT_EQ(i, ProducerToInt(frame_producer.Pass()));
    EXPECT_EQ(stream1, stream.get());
  }

  SpdyFrameType frame_type = DATA;
  scoped_ptr<SpdyBufferProducer> frame_producer;
  base::WeakPtr<SpdyStream> stream;
  EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
}

// Enqueue a bunch of writes and then call
// RemovePendingWritesForStreamsAfter(). No dequeued write should be for
// those streams without a stream id, or with a stream_id after that
// argument.
TEST_F(SpdyWriteQueueTest, RemovePendingWritesForStreamsAfter) {
  SpdyWriteQueue write_queue;

  scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY));
  stream1->set_stream_id(1);
  scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY));
  stream2->set_stream_id(3);
  scoped_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY));
  stream3->set_stream_id(5);
  // No stream id assigned.
  scoped_ptr<SpdyStream> stream4(MakeTestStream(DEFAULT_PRIORITY));
  base::WeakPtr<SpdyStream> streams[] = {
    stream1->GetWeakPtr(), stream2->GetWeakPtr(),
    stream3->GetWeakPtr(), stream4->GetWeakPtr()
  };

  for (int i = 0; i < 100; ++i) {
    write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i),
                        streams[i % arraysize(streams)]);
  }

  write_queue.RemovePendingWritesForStreamsAfter(stream1->stream_id());

  for (int i = 0; i < 100; i += arraysize(streams)) {
    SpdyFrameType frame_type = DATA;
    scoped_ptr<SpdyBufferProducer> frame_producer;
    base::WeakPtr<SpdyStream> stream;
    ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream))
        << "Unable to Dequeue i: " << i;
    EXPECT_EQ(SYN_STREAM, frame_type);
    EXPECT_EQ(i, ProducerToInt(frame_producer.Pass()));
    EXPECT_EQ(stream1, stream.get());
  }

  SpdyFrameType frame_type = DATA;
  scoped_ptr<SpdyBufferProducer> frame_producer;
  base::WeakPtr<SpdyStream> stream;
  EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
}

// Enqueue a bunch of writes and then call Clear(). The write queue
// should clean up the memory properly, and Dequeue() should return
// false.
TEST_F(SpdyWriteQueueTest, Clear) {
  SpdyWriteQueue write_queue;

  for (int i = 0; i < 100; ++i) {
    write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i),
                        base::WeakPtr<SpdyStream>());
  }

  write_queue.Clear();

  SpdyFrameType frame_type = DATA;
  scoped_ptr<SpdyBufferProducer> frame_producer;
  base::WeakPtr<SpdyStream> stream;
  EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
}

}  // namespace

}  // namespace net

/* [<][>][^][v][top][bottom][index][help] */