Chromium Code Reviews| Index: net/websockets/websocket_deflate_stream_test.cc |
| diff --git a/net/websockets/websocket_deflate_stream_test.cc b/net/websockets/websocket_deflate_stream_test.cc |
| index 09b2c5cf1956e5d25838ca2177fcf84008c3bc13..399e74ccc092f8423338b0f81d75ac1609dabd27 100644 |
| --- a/net/websockets/websocket_deflate_stream_test.cc |
| +++ b/net/websockets/websocket_deflate_stream_test.cc |
| @@ -5,6 +5,7 @@ |
| #include "net/websockets/websocket_deflate_stream.h" |
| #include <stdint.h> |
| +#include <deque> |
| #include <string> |
| #include "base/basictypes.h" |
| @@ -15,6 +16,7 @@ |
| #include "net/base/completion_callback.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| +#include "net/websockets/websocket_deflate_predictor.h" |
| #include "net/websockets/websocket_deflater.h" |
| #include "net/websockets/websocket_frame.h" |
| #include "net/websockets/websocket_inflater.h" |
| @@ -65,7 +67,7 @@ std::string ToString(const scoped_refptr<IOBuffer>& buffer, size_t size) { |
| return ToString(buffer.get(), size); |
| } |
| -std::string ToString(WebSocketFrame* frame) { |
| +std::string ToString(const WebSocketFrame* frame) { |
| return frame->data ? ToString(frame->data, frame->header.payload_length) : ""; |
| } |
| @@ -101,14 +103,78 @@ class MockWebSocketStream : public WebSocketStream { |
| MOCK_CONST_METHOD0(GetExtensions, std::string()); |
| }; |
| +// This mock class relies on some assumptions. |
| +// - RecordInputDataFrame is called after the corresponding WriteFrames |
| +// call. |
| +// - RecordWrittenDataFrame is called before writing the frame. |
| +class WebSocketDeflatePredictorMock : public WebSocketDeflatePredictor { |
| + public: |
| + WebSocketDeflatePredictorMock() : result_(DEFLATE) {} |
| + virtual ~WebSocketDeflatePredictorMock() { |
| + DCHECK(frames_to_be_input_.empty()); |
| + DCHECK(frames_to_be_written_.empty()); |
| + } |
| + |
|
Adam Rice
2013/10/30 00:28:07
Please add a comment that these implement the WebS
yhirano
2013/10/30 02:20:33
Done.
|
| + virtual Result Predict(const ScopedVector<WebSocketFrame>& frames, |
| + size_t frame_index) OVERRIDE { |
| + return result_; |
| + } |
| + virtual void RecordInputDataFrame(const WebSocketFrame* frame) OVERRIDE { |
| + DCHECK(WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)); |
| + DCHECK(!frame->header.reserved1); |
| + DCHECK(!frames_to_be_input_.empty()); |
| + DCHECK_EQ(frame, frames_to_be_input_.front()); |
| + frames_to_be_input_.pop_front(); |
| + } |
| + virtual void RecordWrittenDataFrame(const WebSocketFrame* frame) OVERRIDE { |
| + DCHECK(WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)); |
| + frames_to_be_written_.push_back(frame); |
| + } |
| + |
|
Adam Rice
2013/10/30 00:28:07
Please insert a comment here that the following me
yhirano
2013/10/30 02:20:33
Done.
|
| + void set_result(Result result) { result_ = result; } |
| + void AddFrameToBeInput(const WebSocketFrame* frame) { |
| + if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) |
| + return; |
| + frames_to_be_input_.push_back(frame); |
| + } |
| + void AddFramesToBeInput(const ScopedVector<WebSocketFrame>& frames) { |
| + for (size_t i = 0; i < frames.size(); ++i) |
| + AddFrameToBeInput(frames[i]); |
| + } |
| + void VerifySentFrame(const WebSocketFrame* frame) { |
| + if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) |
| + return; |
| + DCHECK(!frames_to_be_written_.empty()); |
| + DCHECK_EQ(frame, frames_to_be_written_.front()); |
| + frames_to_be_written_.pop_front(); |
| + } |
| + void VerifySentFrames(const ScopedVector<WebSocketFrame>& frames) { |
| + for (size_t i = 0; i < frames.size(); ++i) |
| + VerifySentFrame(frames[i]); |
| + } |
| + // Call this method in order to disable checks in the destructor when |
| + // WriteFrames fails. |
| + void Clear() { |
| + frames_to_be_input_.clear(); |
| + frames_to_be_written_.clear(); |
| + } |
| + |
| + private: |
| + Result result_; |
| + std::deque<const WebSocketFrame*> frames_to_be_input_; |
|
Adam Rice
2013/10/30 00:28:07
If I understand correctly, these member variables
yhirano
2013/10/30 02:20:33
Done.
|
| + std::deque<const WebSocketFrame*> frames_to_be_written_; |
| +}; |
| + |
| class WebSocketDeflateStreamTest : public ::testing::Test { |
| public: |
| WebSocketDeflateStreamTest() |
| : mock_stream_(NULL) { |
| mock_stream_ = new testing::StrictMock<MockWebSocketStream>; |
| + predictor_ = new WebSocketDeflatePredictorMock; |
| deflate_stream_.reset(new WebSocketDeflateStream( |
| scoped_ptr<WebSocketStream>(mock_stream_), |
| - WebSocketDeflater::TAKE_OVER_CONTEXT)); |
| + WebSocketDeflater::TAKE_OVER_CONTEXT, |
| + scoped_ptr<WebSocketDeflatePredictor>(predictor_))); |
| } |
| virtual ~WebSocketDeflateStreamTest() {} |
| @@ -116,10 +182,12 @@ class WebSocketDeflateStreamTest : public ::testing::Test { |
| scoped_ptr<WebSocketDeflateStream> deflate_stream_; |
| // |mock_stream_| will be deleted when |deflate_stream_| is destroyed. |
| MockWebSocketStream* mock_stream_; |
| + // |predictor_| will be deleted when |deflate_stream_| is destroyed. |
| + WebSocketDeflatePredictorMock* predictor_; |
| }; |
| // Since WebSocketDeflater with DoNotTakeOverContext is well tested at |
| -// websocket_deflater_test.cc, we have only one test for this configuration |
| +// websocket_deflater_test.cc, we have only a few tests for this configuration |
| // here. |
| class WebSocketDeflateStreamWithDoNotTakeOverContextTest |
| : public ::testing::Test { |
| @@ -127,9 +195,11 @@ class WebSocketDeflateStreamWithDoNotTakeOverContextTest |
| WebSocketDeflateStreamWithDoNotTakeOverContextTest() |
| : mock_stream_(NULL) { |
| mock_stream_ = new testing::StrictMock<MockWebSocketStream>; |
| + predictor_ = new WebSocketDeflatePredictorMock; |
| deflate_stream_.reset(new WebSocketDeflateStream( |
| scoped_ptr<WebSocketStream>(mock_stream_), |
| - WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT)); |
| + WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT, |
| + scoped_ptr<WebSocketDeflatePredictor>(predictor_))); |
| } |
| virtual ~WebSocketDeflateStreamWithDoNotTakeOverContextTest() {} |
| @@ -137,6 +207,8 @@ class WebSocketDeflateStreamWithDoNotTakeOverContextTest |
| scoped_ptr<WebSocketDeflateStream> deflate_stream_; |
| // |mock_stream_| will be deleted when |deflate_stream_| is destroyed. |
| MockWebSocketStream* mock_stream_; |
| + // |predictor_| will be deleted when |deflate_stream_| is destroyed. |
| + WebSocketDeflatePredictorMock* predictor_; |
| }; |
| // ReadFrameStub is a stub for WebSocketStream::ReadFrames. |
| @@ -173,20 +245,21 @@ class ReadFramesStub { |
| ScopedVector<WebSocketFrame>* frames_passed_; |
| }; |
| -// WriteFrameStub is a stub for WebSocketStream::WriteFrames. |
| +// WriteFramesStub is a stub for WebSocketStream::WriteFrames. |
| // It returns |result_| and |frames_| to the caller and |
| // saves |callback| parameter to |callback_|. |
| class WriteFramesStub { |
| public: |
| - explicit WriteFramesStub(int result) : result_(result) {} |
| + explicit WriteFramesStub(WebSocketDeflatePredictorMock* predictor, |
| + int result) |
| + : result_(result), predictor_(predictor) {} |
| int Call(ScopedVector<WebSocketFrame>* frames, |
| const CompletionCallback& callback) { |
| - for (size_t i = 0; i < frames->size(); ++i) { |
| - frames_.push_back((*frames)[i]); |
| - } |
| + frames_.insert(frames_.end(), frames->begin(), frames->end()); |
| frames->weak_clear(); |
| callback_ = callback; |
| + predictor_->VerifySentFrames(frames_); |
| return result_; |
| } |
| @@ -198,6 +271,7 @@ class WriteFramesStub { |
| int result_; |
| CompletionCallback callback_; |
| ScopedVector<WebSocketFrame> frames_; |
| + WebSocketDeflatePredictorMock* predictor_; |
| }; |
| TEST_F(WebSocketDeflateStreamTest, ReadFailedImmediately) { |
| @@ -791,14 +865,17 @@ TEST_F(WebSocketDeflateStreamTest, WriteFailedImmediately) { |
| } |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "hello"); |
| + predictor_->AddFramesToBeInput(frames); |
| EXPECT_EQ(ERR_FAILED, deflate_stream_->WriteFrames(&frames, callback)); |
| + predictor_->Clear(); |
| } |
| TEST_F(WebSocketDeflateStreamTest, WriteFrameImmediately) { |
| ScopedVector<WebSocketFrame> frames; |
| CompletionCallback callback; |
| - WriteFramesStub stub(OK); |
| + WriteFramesStub stub(predictor_, OK); |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); |
| + predictor_->AddFramesToBeInput(frames); |
| { |
| InSequence s; |
| EXPECT_CALL(*mock_stream_, WriteFrames(_, _)) |
| @@ -815,7 +892,7 @@ TEST_F(WebSocketDeflateStreamTest, WriteFrameImmediately) { |
| } |
| TEST_F(WebSocketDeflateStreamTest, WriteFrameAsync) { |
| - WriteFramesStub stub(ERR_IO_PENDING); |
| + WriteFramesStub stub(predictor_, ERR_IO_PENDING); |
| MockCallback mock_callback, checkpoint; |
| CompletionCallback callback = |
| base::Bind(&MockCallback::Call, base::Unretained(&mock_callback)); |
| @@ -828,6 +905,7 @@ TEST_F(WebSocketDeflateStreamTest, WriteFrameAsync) { |
| EXPECT_CALL(mock_callback, Call(OK)); |
| } |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); |
| + predictor_->AddFramesToBeInput(frames); |
| ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->WriteFrames(&frames, callback)); |
| checkpoint.Call(0); |
| @@ -847,7 +925,8 @@ TEST_F(WebSocketDeflateStreamTest, WriteControlFrameBetweenDataFrames) { |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "Hel"); |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodePing, kFinal); |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "lo"); |
| - WriteFramesStub stub(OK); |
| + predictor_->AddFramesToBeInput(frames); |
| + WriteFramesStub stub(predictor_, OK); |
| CompletionCallback callback; |
| { |
| @@ -871,7 +950,8 @@ TEST_F(WebSocketDeflateStreamTest, WriteControlFrameBetweenDataFrames) { |
| TEST_F(WebSocketDeflateStreamTest, WriteEmptyMessage) { |
| ScopedVector<WebSocketFrame> frames; |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal); |
| - WriteFramesStub stub(OK); |
| + predictor_->AddFramesToBeInput(frames); |
| + WriteFramesStub stub(predictor_, OK); |
| CompletionCallback callback; |
| { |
| @@ -888,10 +968,39 @@ TEST_F(WebSocketDeflateStreamTest, WriteEmptyMessage) { |
| EXPECT_EQ(std::string("\x02\x00", 2), ToString(frames_passed[0])); |
| } |
| +TEST_F(WebSocketDeflateStreamTest, WriteUncompressedMessage) { |
| + ScopedVector<WebSocketFrame> frames; |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "AAAA"); |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "AAA"); |
| + predictor_->AddFramesToBeInput(frames); |
| + WriteFramesStub stub(predictor_, OK); |
| + CompletionCallback callback; |
| + |
| + predictor_->set_result(WebSocketDeflatePredictor::DO_NOT_DEFLATE); |
| + |
| + { |
| + InSequence s; |
| + EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) |
| + .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); |
| + } |
| + ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); |
| + const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); |
| + ASSERT_EQ(2u, frames_passed.size()); |
| + EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); |
| + EXPECT_FALSE(frames_passed[0]->header.final); |
| + EXPECT_FALSE(frames_passed[0]->header.reserved1); |
| + EXPECT_EQ("AAAA", ToString(frames_passed[0])); |
| + EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, |
| + frames_passed[1]->header.opcode); |
| + EXPECT_TRUE(frames_passed[1]->header.final); |
| + EXPECT_FALSE(frames_passed[1]->header.reserved1); |
| + EXPECT_EQ("AAA", ToString(frames_passed[1])); |
| +} |
| + |
| TEST_F(WebSocketDeflateStreamTest, LargeDeflatedFramesShouldBeSplit) { |
| WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT); |
| LinearCongruentialGenerator lcg(133); |
| - WriteFramesStub stub(OK); |
| + WriteFramesStub stub(predictor_, OK); |
| CompletionCallback callback; |
| const size_t size = 1024; |
| @@ -912,9 +1021,11 @@ TEST_F(WebSocketDeflateStreamTest, LargeDeflatedFramesShouldBeSplit) { |
| deflater.AddBytes(data.data(), data.size()); |
| FrameFlag flag = is_final ? kFinal : kNoFlag; |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeBinary, flag, data); |
| + predictor_->AddFramesToBeInput(frames); |
| ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); |
| - for (size_t i = 0; i < stub.frames()->size(); ++i) |
| - total_compressed_frames.push_back((*stub.frames())[i]); |
| + total_compressed_frames.insert(total_compressed_frames.end(), |
| + stub.frames()->begin(), |
| + stub.frames()->end()); |
| stub.frames()->weak_clear(); |
| if (is_final) |
| break; |
| @@ -945,7 +1056,8 @@ TEST_F(WebSocketDeflateStreamTest, WriteMultipleMessages) { |
| ScopedVector<WebSocketFrame> frames; |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); |
| - WriteFramesStub stub(OK); |
| + predictor_->AddFramesToBeInput(frames); |
| + WriteFramesStub stub(predictor_, OK); |
| CompletionCallback callback; |
| { |
| @@ -972,7 +1084,8 @@ TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest, |
| ScopedVector<WebSocketFrame> frames; |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); |
| AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); |
| - WriteFramesStub stub(OK); |
| + predictor_->AddFramesToBeInput(frames); |
| + WriteFramesStub stub(predictor_, OK); |
| CompletionCallback callback; |
| { |
| @@ -995,6 +1108,58 @@ TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest, |
| ToString(frames_passed[1])); |
| } |
| +// In order to check the stream works correctly for multiple |
| +// "PossiblyCompressedMessage"s, we test various messages at one test case. |
| +TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest, |
| + WritePossiblyCompressMessages) { |
| + ScopedVector<WebSocketFrame> frames; |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "He"); |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "llo"); |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "AAAAAAAAAA"); |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "AA"); |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "XX"); |
| + AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "YY"); |
| + predictor_->AddFramesToBeInput(frames); |
| + WriteFramesStub stub(predictor_, OK); |
| + CompletionCallback callback; |
| + predictor_->set_result(WebSocketDeflatePredictor::TRY_DEFLATE); |
| + |
| + { |
| + InSequence s; |
| + EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) |
| + .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); |
| + } |
| + ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); |
| + const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); |
| + ASSERT_EQ(5u, frames_passed.size()); |
| + |
| + EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); |
| + EXPECT_FALSE(frames_passed[0]->header.final); |
| + EXPECT_FALSE(frames_passed[0]->header.reserved1); |
| + EXPECT_EQ("He", ToString(frames_passed[0])); |
| + EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, |
| + frames_passed[1]->header.opcode); |
| + EXPECT_TRUE(frames_passed[1]->header.final); |
| + EXPECT_FALSE(frames_passed[1]->header.reserved1); |
| + EXPECT_EQ("llo", ToString(frames_passed[1])); |
| + |
| + EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[2]->header.opcode); |
| + EXPECT_TRUE(frames_passed[2]->header.final); |
| + EXPECT_TRUE(frames_passed[2]->header.reserved1); |
| + EXPECT_EQ(std::string("\x72\x74\x44\x00\x00\x00", 6), |
| + ToString(frames_passed[2])); |
| + |
| + EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[3]->header.opcode); |
| + EXPECT_FALSE(frames_passed[3]->header.final); |
| + EXPECT_FALSE(frames_passed[3]->header.reserved1); |
| + EXPECT_EQ("XX", ToString(frames_passed[3])); |
| + EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, |
| + frames_passed[4]->header.opcode); |
| + EXPECT_TRUE(frames_passed[4]->header.final); |
| + EXPECT_FALSE(frames_passed[4]->header.reserved1); |
| + EXPECT_EQ("YY", ToString(frames_passed[4])); |
| +} |
| + |
| } // namespace |
| } // namespace net |