blob: 72638cf7ed2071fbb9494cf1682d07305f9dbade [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: morlovich@google.com (Maksim Orlovich)
#include "pagespeed/kernel/base/chunking_writer.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/mock_message_handler.h"
#include "pagespeed/kernel/base/null_mutex.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/writer.h"
namespace net_instaweb {
class MessageHandler;
namespace {
// Records a traces of writes and flushes performed into recorded()
// as follows:
// 1) Write of "text" will append: W:text|
// 2) A flush will append: F|
//
// Also makes sure the passed in handler is correct, and let's one
// trigger failures on a given operation.
class TracingWriter : public Writer {
public:
explicit TracingWriter(MessageHandler* expected_handler) :
expected_handler_(expected_handler), ops_(0), fail_on_op_(-1) {
}
virtual bool Write(const StringPiece& str, MessageHandler* handler) {
EXPECT_EQ(expected_handler_, handler);
if (ops_ == fail_on_op_) {
// Still advance, so that we know we don't get called again
++ops_;
return false;
}
++ops_;
recorded_.append("W:");
recorded_.append(str.data(), str.size());
recorded_.append("|");
return true;
}
virtual bool Flush(MessageHandler* handler) {
EXPECT_EQ(expected_handler_, handler);
if (ops_ == fail_on_op_) {
// Still advance, so that we know we don't get called again
++ops_;
return false;
}
++ops_;
recorded_.append("F|");
return true;
}
const GoogleString& recorded() const { return recorded_; }
// Tells this filter to report a failure on n'th invocation exactly.
// (starting from 0)
void set_fail_on_op(int n) { fail_on_op_ = n; }
private:
MessageHandler* expected_handler_;
GoogleString recorded_;
int ops_;
int fail_on_op_;
};
class ChunkingWriterTest : public testing::Test {
public:
ChunkingWriterTest() : message_handler_(new NullMutex) {}
virtual void SetUp() {
tracer_.reset(new TracingWriter(&message_handler_));
SetUpWithLimit(0);
}
void SetUpWithLimit(int limit) {
chunker_.reset(new ChunkingWriter(tracer_.get(), limit));
}
protected:
MockMessageHandler message_handler_;
scoped_ptr<TracingWriter> tracer_;
scoped_ptr<ChunkingWriter> chunker_;
};
TEST_F(ChunkingWriterTest, UnchunkedBasic) {
EXPECT_TRUE(chunker_->Write("abc", &message_handler_));
EXPECT_TRUE(chunker_->Write("def", &message_handler_));
EXPECT_TRUE(chunker_->Flush(&message_handler_));
EXPECT_EQ("W:abc|W:def|F|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, ChunkedBasic) {
SetUpWithLimit(2);
EXPECT_TRUE(chunker_->Write("abc", &message_handler_));
EXPECT_TRUE(chunker_->Write("def", &message_handler_));
EXPECT_TRUE(chunker_->Flush(&message_handler_));
EXPECT_EQ("W:ab|F|W:c|W:d|F|W:ef|F|F|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, ChunkedBasicLong) {
SetUpWithLimit(4);
EXPECT_TRUE(chunker_->Write("abcdefghijklmnopqrs", &message_handler_));
EXPECT_TRUE(chunker_->Flush(&message_handler_));
EXPECT_EQ("W:abcd|F|W:efgh|F|W:ijkl|F|W:mnop|F|W:qrs|F|",
tracer_->recorded());
}
TEST_F(ChunkingWriterTest, ChunkedManualFlush) {
SetUpWithLimit(4);
EXPECT_TRUE(chunker_->Write("abc", &message_handler_));
EXPECT_TRUE(chunker_->Flush(&message_handler_));
EXPECT_TRUE(chunker_->Write("defgh", &message_handler_));
EXPECT_EQ("W:abc|F|W:defg|F|W:h|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, UnchunkedFailureProp1) {
tracer_->set_fail_on_op(1);
EXPECT_TRUE(chunker_->Write("abc", &message_handler_));
EXPECT_FALSE(chunker_->Write("def", &message_handler_));
EXPECT_EQ("W:abc|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, UnchunkedFailureProp2) {
tracer_->set_fail_on_op(2);
EXPECT_TRUE(chunker_->Write("abc", &message_handler_));
EXPECT_TRUE(chunker_->Write("def", &message_handler_));
EXPECT_FALSE(chunker_->Flush(&message_handler_));
EXPECT_EQ("W:abc|W:def|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, ChunkedFailureProp1) {
tracer_->set_fail_on_op(1);
SetUpWithLimit(4);
EXPECT_FALSE(chunker_->Write("abcdefgh", &message_handler_));
EXPECT_EQ("W:abcd|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, ChunkedFailureProp2) {
tracer_->set_fail_on_op(2);
SetUpWithLimit(4);
EXPECT_FALSE(chunker_->Write("abcdefgh", &message_handler_));
EXPECT_EQ("W:abcd|F|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, ChunkedFailureProp3) {
tracer_->set_fail_on_op(3);
SetUpWithLimit(4);
EXPECT_FALSE(chunker_->Write("abcdefgh", &message_handler_));
EXPECT_EQ("W:abcd|F|W:efgh|", tracer_->recorded());
}
TEST_F(ChunkingWriterTest, ChunkedFailureProp4) {
tracer_->set_fail_on_op(4);
SetUpWithLimit(4);
EXPECT_TRUE(chunker_->Write("abcdefgh", &message_handler_));
EXPECT_EQ("W:abcd|F|W:efgh|F|", tracer_->recorded());
}
} // namespace
} // namespace net_instaweb