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 |