blob: d650ef8b048aba48cf7c88e36e71fb67bca6a3de [file] [log] [blame]
Avi Drissman3e1a26c2022-09-15 20:26:031// Copyright 2016 The Chromium Authors
dschuyler613a1032016-12-15 19:22:352// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Arthur Sonzogni24d53e32024-07-26 14:00:545#ifdef UNSAFE_BUFFERS_BUILD
6// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
7#pragma allow_unsafe_buffers
8#endif
9
dschuyler613a1032016-12-15 19:22:3510#include <utility>
11
Keishi Hattori0e45c022021-11-27 09:25:5212#include "base/memory/raw_ptr.h"
dschuyler613a1032016-12-15 19:22:3513#include "net/base/io_buffer.h"
14#include "net/base/test_completion_callback.h"
15#include "net/filter/mock_source_stream.h"
16#include "testing/gtest/include/gtest/gtest.h"
Reilly Grantf31ae652017-11-16 20:40:0417#include "ui/base/webui/i18n_source_stream.h"
dschuyler613a1032016-12-15 19:22:3518
Reilly Grantf31ae652017-11-16 20:40:0419namespace ui {
dschuyler613a1032016-12-15 19:22:3520
21namespace {
22
dschuyleread12ef2017-01-10 02:36:1323// This constant is rather arbitrary, though the offsets and other sizes must
dschuyler613a1032016-12-15 19:22:3524// be less than kBufferSize.
25const int kBufferSize = 256;
dschuyler613a1032016-12-15 19:22:3526
dschuyleread12ef2017-01-10 02:36:1327const int kMinimumSize = 1;
28const int kSmallSize = 5; // Arbitrary small value > 1.
29const int kInOneReadSize = INT_MAX;
dschuyler613a1032016-12-15 19:22:3530
dschuyleread12ef2017-01-10 02:36:1331struct I18nTest {
32 constexpr I18nTest(const char* input, const char* expected_output)
33 : input(input), expected_output(expected_output) {}
dschuyler613a1032016-12-15 19:22:3534
dschuyleread12ef2017-01-10 02:36:1335 const char* input;
36 const char* expected_output;
37};
38
39constexpr I18nTest kTestEmpty = I18nTest("", "");
40
41constexpr I18nTest kTestNoReplacements =
42 I18nTest("This text has no i18n replacements.",
43 "This text has no i18n replacements.");
44
45constexpr I18nTest kTestTagAtEndOfLine =
46 I18nTest("test with tag at end of line $",
47 "test with tag at end of line $");
48
49constexpr I18nTest kTestOneReplacement = I18nTest("$i18n{alpha}", "apple");
50
51constexpr I18nTest kTestOneReplacementPlus =
52 I18nTest("Extra text $i18n{alpha}.", "Extra text apple.");
53
54constexpr I18nTest kTestThreeReplacements =
55 I18nTest("$i18n{alpha}^$i18n{beta}_$i18n{gamma}", "apple^banana_carrot");
56
57constexpr I18nTest kTestExtraBraces =
58 I18nTest("($i18n{alpha})^_^_^_^_$i18n{beta}_beta_$i18n{gamma}}}}}}",
59 "(apple)^_^_^_^_banana_beta_carrot}}}}}");
60
61// These tests with generic names are sequences that might catch an error in the
62// future, depending on how the code changes.
63constexpr I18nTest kTest1 =
64 I18nTest(" } $($i18n{gamma})^_^_^_^_$i18n{alpha}_$i18n{gamma}$",
65 " } $(carrot)^_^_^_^_apple_carrot$");
66
67constexpr I18nTest kTest2 =
68 I18nTest("$i18n{alpha} gamma}{ ^_^_^_^_$abc{beta}:$i18n{gamma}z",
69 "apple gamma}{ ^_^_^_^_$abc{beta}:carrotz");
dschuyler613a1032016-12-15 19:22:3570
71struct I18nTestParam {
dschuyleread12ef2017-01-10 02:36:1372 constexpr I18nTestParam(
73 const I18nTest* test,
74 int buf_size,
75 int read_size,
76 net::MockSourceStream::Mode read_mode = net::MockSourceStream::SYNC)
77 : buffer_size(buf_size),
78 read_size(read_size),
79 mode(read_mode),
80 test(test) {}
dschuyler613a1032016-12-15 19:22:3581
82 const int buffer_size;
dschuyleread12ef2017-01-10 02:36:1383 const int read_size;
dschuyler613a1032016-12-15 19:22:3584 const net::MockSourceStream::Mode mode;
Bartek Nowierskif5eeeba2024-01-25 12:49:3985 raw_ptr<const I18nTest> test;
dschuyler613a1032016-12-15 19:22:3586};
87
88} // namespace
89
90class I18nSourceStreamTest : public ::testing::TestWithParam<I18nTestParam> {
91 protected:
92 I18nSourceStreamTest() : output_buffer_size_(GetParam().buffer_size) {}
93
94 // Helpful function to initialize the test fixture.
95 void Init() {
Tom Sepez12abc682023-11-01 17:30:1996 output_buffer_ =
97 base::MakeRefCounted<net::IOBufferWithSize>(output_buffer_size_);
dschuyler613a1032016-12-15 19:22:3598 std::unique_ptr<net::MockSourceStream> source(new net::MockSourceStream());
99 source_ = source.get();
100
dschuyleread12ef2017-01-10 02:36:13101 replacements_["alpha"] = "apple";
102 replacements_["beta"] = "banana";
103 replacements_["gamma"] = "carrot";
dschuyler613a1032016-12-15 19:22:35104 stream_ = I18nSourceStream::Create(
Tsuyoshi Horo17846bf2025-02-20 23:28:13105 std::move(source), net::SourceStreamType::kNone, &replacements_);
dschuyler613a1032016-12-15 19:22:35106 }
107
108 // If MockSourceStream::Mode is ASYNC, completes 1 read from |mock_stream| and
109 // wait for |callback| to complete. If Mode is not ASYNC, does nothing and
110 // returns |previous_result|.
111 int CompleteReadIfAsync(int previous_result,
112 net::TestCompletionCallback* callback,
113 net::MockSourceStream* mock_stream) {
114 if (GetParam().mode == net::MockSourceStream::ASYNC) {
115 EXPECT_EQ(net::ERR_IO_PENDING, previous_result);
116 mock_stream->CompleteNextRead();
117 return callback->WaitForResult();
118 }
119 return previous_result;
120 }
121
dschuyler613a1032016-12-15 19:22:35122 net::IOBuffer* output_buffer() { return output_buffer_.get(); }
123 char* output_data() { return output_buffer_->data(); }
124 size_t output_buffer_size() { return output_buffer_size_; }
125
126 net::MockSourceStream* source() { return source_; }
127 I18nSourceStream* stream() { return stream_.get(); }
128
dschuyleread12ef2017-01-10 02:36:13129 void PushReadResults(const char* input, size_t chunk_size) {
130 size_t written = 0;
131 size_t source_size = strlen(GetParam().test->input);
132 while (written != source_size) {
133 size_t write_size = std::min(chunk_size, source_size - written);
134 source()->AddReadResult(input + written, write_size, net::OK,
135 GetParam().mode);
136 written += write_size;
137 }
138 source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode);
139 }
140
dschuyler613a1032016-12-15 19:22:35141 // Reads from |stream_| until an error occurs or the EOF is reached.
142 // When an error occurs, returns the net error code. When an EOF is reached,
143 // returns the number of bytes read and appends data read to |output|.
144 int ReadStream(std::string* output) {
145 int bytes_read = 0;
146 while (true) {
147 net::TestCompletionCallback callback;
148 int rv = stream_->Read(output_buffer(), output_buffer_size(),
149 callback.callback());
150 if (rv == net::ERR_IO_PENDING)
151 rv = CompleteReadIfAsync(rv, &callback, source());
152 if (rv == net::OK)
153 break;
154 if (rv < net::OK)
155 return rv;
156 EXPECT_GT(rv, net::OK);
157 bytes_read += rv;
158 output->append(output_data(), rv);
159 }
160 return bytes_read;
161 }
162
163 private:
dschuyler613a1032016-12-15 19:22:35164 scoped_refptr<net::IOBuffer> output_buffer_;
165 const int output_buffer_size_;
166
Tom Sepezc42bc802023-02-10 23:56:49167 std::unique_ptr<I18nSourceStream> stream_; // Must outlive `source_`.
Keishi Hattori0e45c022021-11-27 09:25:52168 raw_ptr<net::MockSourceStream> source_;
dschuyler613a1032016-12-15 19:22:35169
Reilly Grantf31ae652017-11-16 20:40:04170 TemplateReplacements replacements_;
dschuyler613a1032016-12-15 19:22:35171};
172
Victor Costanbe7d15f2019-01-27 03:19:40173INSTANTIATE_TEST_SUITE_P(
dschuyler613a1032016-12-15 19:22:35174 I18nSourceStreamTests,
175 I18nSourceStreamTest,
dschuyleread12ef2017-01-10 02:36:13176 ::testing::Values(