| // Copyright 2016 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 <utility> |
| |
| #include "net/base/io_buffer.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/filter/mock_source_stream.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/webui/i18n_source_stream.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| // This constant is rather arbitrary, though the offsets and other sizes must |
| // be less than kBufferSize. |
| const int kBufferSize = 256; |
| |
| const int kMinimumSize = 1; |
| const int kSmallSize = 5; // Arbitrary small value > 1. |
| const int kInOneReadSize = INT_MAX; |
| |
| struct I18nTest { |
| constexpr I18nTest(const char* input, const char* expected_output) |
| : input(input), expected_output(expected_output) {} |
| |
| const char* input; |
| const char* expected_output; |
| }; |
| |
| constexpr I18nTest kTestEmpty = I18nTest("", ""); |
| |
| constexpr I18nTest kTestNoReplacements = |
| I18nTest("This text has no i18n replacements.", |
| "This text has no i18n replacements."); |
| |
| constexpr I18nTest kTestTagAtEndOfLine = |
| I18nTest("test with tag at end of line $", |
| "test with tag at end of line $"); |
| |
| constexpr I18nTest kTestOneReplacement = I18nTest("$i18n{alpha}", "apple"); |
| |
| constexpr I18nTest kTestOneReplacementPlus = |
| I18nTest("Extra text $i18n{alpha}.", "Extra text apple."); |
| |
| constexpr I18nTest kTestThreeReplacements = |
| I18nTest("$i18n{alpha}^$i18n{beta}_$i18n{gamma}", "apple^banana_carrot"); |
| |
| constexpr I18nTest kTestExtraBraces = |
| I18nTest("($i18n{alpha})^_^_^_^_$i18n{beta}_beta_$i18n{gamma}}}}}}", |
| "(apple)^_^_^_^_banana_beta_carrot}}}}}"); |
| |
| // These tests with generic names are sequences that might catch an error in the |
| // future, depending on how the code changes. |
| constexpr I18nTest kTest1 = |
| I18nTest(" } $($i18n{gamma})^_^_^_^_$i18n{alpha}_$i18n{gamma}$", |
| " } $(carrot)^_^_^_^_apple_carrot$"); |
| |
| constexpr I18nTest kTest2 = |
| I18nTest("$i18n{alpha} gamma}{ ^_^_^_^_$abc{beta}:$i18n{gamma}z", |
| "apple gamma}{ ^_^_^_^_$abc{beta}:carrotz"); |
| |
| struct I18nTestParam { |
| constexpr I18nTestParam( |
| const I18nTest* test, |
| int buf_size, |
| int read_size, |
| net::MockSourceStream::Mode read_mode = net::MockSourceStream::SYNC) |
| : buffer_size(buf_size), |
| read_size(read_size), |
| mode(read_mode), |
| test(test) {} |
| |
| const int buffer_size; |
| const int read_size; |
| const net::MockSourceStream::Mode mode; |
| const I18nTest* test; |
| }; |
| |
| } // namespace |
| |
| class I18nSourceStreamTest : public ::testing::TestWithParam<I18nTestParam> { |
| protected: |
| I18nSourceStreamTest() : output_buffer_size_(GetParam().buffer_size) {} |
| |
| // Helpful function to initialize the test fixture. |
| void Init() { |
| output_buffer_ = base::MakeRefCounted<net::IOBuffer>(output_buffer_size_); |
| std::unique_ptr<net::MockSourceStream> source(new net::MockSourceStream()); |
| source_ = source.get(); |
| |
| replacements_["alpha"] = "apple"; |
| replacements_["beta"] = "banana"; |
| replacements_["gamma"] = "carrot"; |
| stream_ = I18nSourceStream::Create( |
| std::move(source), net::SourceStream::TYPE_NONE, &replacements_); |
| } |
| |
| // If MockSourceStream::Mode is ASYNC, completes 1 read from |mock_stream| and |
| // wait for |callback| to complete. If Mode is not ASYNC, does nothing and |
| // returns |previous_result|. |
| int CompleteReadIfAsync(int previous_result, |
| net::TestCompletionCallback* callback, |
| net::MockSourceStream* mock_stream) { |
| if (GetParam().mode == net::MockSourceStream::ASYNC) { |
| EXPECT_EQ(net::ERR_IO_PENDING, previous_result); |
| mock_stream->CompleteNextRead(); |
| return callback->WaitForResult(); |
| } |
| return previous_result; |
| } |
| |
| net::IOBuffer* output_buffer() { return output_buffer_.get(); } |
| char* output_data() { return output_buffer_->data(); } |
| size_t output_buffer_size() { return output_buffer_size_; } |
| |
| net::MockSourceStream* source() { return source_; } |
| I18nSourceStream* stream() { return stream_.get(); } |
| |
| void PushReadResults(const char* input, size_t chunk_size) { |
| size_t written = 0; |
| size_t source_size = strlen(GetParam().test->input); |
| while (written != source_size) { |
| size_t write_size = std::min(chunk_size, source_size - written); |
| source()->AddReadResult(input + written, write_size, net::OK, |
| GetParam().mode); |
| written += write_size; |
| } |
| source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode); |
| } |
| |
| // Reads from |stream_| until an error occurs or the EOF is reached. |
| // When an error occurs, returns the net error code. When an EOF is reached, |
| // returns the number of bytes read and appends data read to |output|. |
| int ReadStream(std::string* output) { |
| int bytes_read = 0; |
| while (true) { |
| net::TestCompletionCallback callback; |
| int rv = stream_->Read(output_buffer(), output_buffer_size(), |
| callback.callback()); |
| if (rv == net::ERR_IO_PENDING) |
| rv = CompleteReadIfAsync(rv, &callback, source()); |
| if (rv == net::OK) |
| break; |
| if (rv < net::OK) |
| return rv; |
| EXPECT_GT(rv, net::OK); |
| bytes_read += rv; |
| output->append(output_data(), rv); |
| } |
| return bytes_read; |
| } |
| |
| private: |
| scoped_refptr<net::IOBuffer> output_buffer_; |
| const int output_buffer_size_; |
| |
| net::MockSourceStream* source_; |
| std::unique_ptr<I18nSourceStream> stream_; |
| |
| TemplateReplacements replacements_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| I18nSourceStreamTests, |
| I18nSourceStreamTest, |
| ::testing::Values( |
| I18nTestParam(&kTest1, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTest1, kBufferSize, kSmallSize), |
| I18nTestParam(&kTest1, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTest1, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTest2, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTest2, kBufferSize, kSmallSize), |
| I18nTestParam(&kTest2, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTest2, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTestEmpty, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTestEmpty, kBufferSize, kSmallSize), |
| I18nTestParam(&kTestEmpty, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTestEmpty, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTestExtraBraces, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTestExtraBraces, kBufferSize, kSmallSize), |
| I18nTestParam(&kTestExtraBraces, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTestExtraBraces, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTestNoReplacements, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTestNoReplacements, kBufferSize, kSmallSize), |
| I18nTestParam(&kTestNoReplacements, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTestNoReplacements, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTestOneReplacement, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTestOneReplacement, kBufferSize, kSmallSize), |
| I18nTestParam(&kTestOneReplacement, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTestOneReplacement, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTestOneReplacementPlus, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTestOneReplacementPlus, kBufferSize, kSmallSize), |
| I18nTestParam(&kTestOneReplacementPlus, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTestOneReplacementPlus, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTestTagAtEndOfLine, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTestTagAtEndOfLine, kBufferSize, kSmallSize), |
| I18nTestParam(&kTestTagAtEndOfLine, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTestTagAtEndOfLine, kMinimumSize, kSmallSize), |
| |
| I18nTestParam(&kTestThreeReplacements, kBufferSize, kInOneReadSize), |
| I18nTestParam(&kTestThreeReplacements, kBufferSize, kSmallSize), |
| I18nTestParam(&kTestThreeReplacements, kMinimumSize, kMinimumSize), |
| I18nTestParam(&kTestThreeReplacements, kMinimumSize, kSmallSize))); |
| |
| TEST_P(I18nSourceStreamTest, FilterTests) { |
| Init(); |
| // Create the chain of read buffers. |
| PushReadResults(GetParam().test->input, GetParam().read_size); |
| |
| // Process the buffers. |
| std::string actual_output; |
| int rv = ReadStream(&actual_output); |
| |
| // Check the results. |
| std::string expected_output(GetParam().test->expected_output); |
| EXPECT_EQ(expected_output.size(), static_cast<size_t>(rv)); |
| EXPECT_EQ(expected_output, actual_output); |
| EXPECT_EQ("i18n", stream()->Description()); |
| } |
| |
| TEST_P(I18nSourceStreamTest, LargeFilterTests) { |
| Init(); |
| std::string padding; |
| // 251 and 599 are prime and avoid power-of-two repetition. |
| int padding_modulus = 251; |
| int pad_size = 599; |
| padding.resize(pad_size); |
| for (int i = 0; i < pad_size; ++i) |
| padding[i] = i % padding_modulus; |
| |
| // Create the chain of read buffers. |
| const int kPadCount = 128; // Arbitrary number of pads to add. |
| for (int i = 0; i < kPadCount; ++i) { |
| source()->AddReadResult(padding.c_str(), padding.size(), net::OK, |
| GetParam().mode); |
| } |
| PushReadResults(GetParam().test->input, GetParam().read_size); |
| |
| // Process the buffers. |
| std::string actual_output; |
| int rv = ReadStream(&actual_output); |
| |
| // Check the results. |
| size_t total_padding = kPadCount * padding.size(); |
| std::string expected_output(GetParam().test->expected_output); |
| ASSERT_EQ(expected_output.size() + total_padding, static_cast<size_t>(rv)); |
| for (int i = 0; i < kPadCount; ++i) { |
| EXPECT_EQ(actual_output.substr(i * padding.size(), padding.size()), |
| padding); |
| } |
| EXPECT_EQ(expected_output, &actual_output[total_padding]); |
| EXPECT_EQ("i18n", stream()->Description()); |
| } |
| |
| } // namespace ui |