File: tab_hover_card_bubble_view_unittest.cc

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (368 lines) | stat: -rw-r--r-- 17,178 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"

#include <ostream>
#include <string>

#include "build/build_config.h"
#include "chrome/browser/ui/views/tabs/filename_elider.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest-param-test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/render_text.h"

#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif

class TabHoverCardBubbleViewFilenameEliderTest {
 protected:
  static constexpr float kGlyphWidth = 10.0;
  static constexpr float kGlyphHeight = 10.0;

  static std::unique_ptr<gfx::RenderText> CreateRenderText(
      const std::u16string& text) {
    auto render_text = gfx::RenderText::CreateRenderText();
    render_text->set_glyph_width_for_test(kGlyphWidth);
    render_text->set_glyph_height_for_test(kGlyphHeight);
    if (!text.empty()) {
      render_text->SetText(text);
    }
    return render_text;
  }

  static gfx::Rect GetTextRect(int num_chars_per_line) {
    // Bump up the width to almost large enough to hold an extra character in
    // order to avoid some quirkiness with RenderText even when there's a fixed
    // glyph width.
    return gfx::Rect(kGlyphWidth * (num_chars_per_line + 0.9f),
                     kGlyphHeight * 2);
  }

  static FilenameElider::LineLengths GetLineLengths(const std::u16string& text,
                                                    int num_chars_per_line) {
    FilenameElider elider(CreateRenderText(text));
    return elider.GetLineLengths(GetTextRect(num_chars_per_line));
  }

  static std::u16string ElideImpl(const std::u16string& text,
                                  size_t max_first_line_length,
                                  size_t max_second_line_length) {
    FilenameElider elider(CreateRenderText(text));
    return elider.ElideImpl(FilenameElider::LineLengths{
        max_first_line_length, max_second_line_length});
  }

 private:
  // Required for loading fallback fonts, as fallback font loading needs to
  // happen on the UI thread.
  content::BrowserTaskEnvironment task_environment_;
};

#define EM_SPACE u"\u2003"
#define MULT_SYMBOL u"\u00D7"
#define COMBINING_CIRCUMFLEX u"\u0302"

#define MEDICAL_SYMBOL_EMOJI u"\u2695\uFE0F"
#define ZERO_WIDTH_JOINER u"\u200D"
#define MAN_EMOJI u"\U0001F468"
#define MEDIUM_SKIN_TONE_MODIFIER u"\U0001F3FD"
#define MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE \
  MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER ZERO_WIDTH_JOINER MEDICAL_SYMBOL_EMOJI

#define BIDIFY(a, b) a u"\n\u2068" b u"\u2069"
#define ELLIPSIZE(a, b) BIDIFY(a, u"\u2026" b)

struct ElideImplTestParams {
  const char16_t* const text;
  const size_t max_first_line_length;
  const size_t max_second_line_length;
  const char16_t* const expected;
  const char* const comment;
};

void PrintTo(const ElideImplTestParams& params, ::std::ostream* os) {
  *os << params.comment << " (\"" << params.text << "\", "
      << params.max_first_line_length << ", " << params.max_second_line_length
      << ")";
}

const ElideImplTestParams kElideImplTestParams[]{
    {u"", 0, 0, u"", "Zero-length string yields empty result."},
    {u"abcd", 0, 0, u"", "Zero-length lines yield empty results."},
    {u"abcd", 4, 0, u"abcd",
     "First line is all text yields full text, even if second is empty."},
    {u"abcd", 0, 4, u"abcd",
     "Second line is all text yields full text, even if first is empty."},
    {u"abcd", 4, 1, u"abcd", "First line is all text yields full text."},
    {u"abcd", 1, 4, u"abcd", "Second line is all text yields full text."},
    {u"abcd", 1, 1, ELLIPSIZE(u"a", u"d"),
     "Gap between first and second lines."},
    {u"abcd", 2, 2, u"ab\ncd", "No gap between first and second lines."},
    {u"abcd", 3, 3, u"abc\nd", "Overlap between first and second lines."},
    {u"abc.def", 3, 3, ELLIPSIZE(u"abc", u"def"), "Extension dot is cut out."},
    {u"abc.def", 4, 3, u"abc.\ndef",
     "Extension whole string dot is not moved."},
    {u"abc.def", 4, 4, BIDIFY(u"abc", u".def"),
     "Extension overlap dot moved to second line."},
    {u"abc.def", 6, 6, BIDIFY(u"abc", u".def"),
     "Extension overlap dot moved to second line (2)."},
    {u"abc def", 3, 4, u"abc\ndef", "Whitespace after break is elided (1)."},
    {u"abc\t" EM_SPACE u"def", 3, 4, ELLIPSIZE(u"abc", u"def"),
     "Whitespace after break is elided (2)."},
    {u"abc\t" EM_SPACE u"def", 3, 5, u"abc\ndef",
     "Whitespace after break is elided (3)."},
    {u"abc def", 2, 4, ELLIPSIZE(u"ab", u"def"),
     "Whitespace after ellipsis is elided (1)."},
    {u"abc\t" EM_SPACE u"def", 2, 4, ELLIPSIZE(u"ab", u"def"),
     "Whitespace after ellipsis is elided (2)."},
    {u"abc\t" EM_SPACE u"def", 2, 5, ELLIPSIZE(u"ab", u"def"),
     "Whitespace after ellipsis is elided (3)."},
    {u"abco" COMBINING_CIRCUMFLEX u"def", 3, 5,
     u"abc\no" COMBINING_CIRCUMFLEX u"def",
     "Cut before combining characters does not elide characters."},
    {u"abco" COMBINING_CIRCUMFLEX u"def", 5, 3,
     u"abco" COMBINING_CIRCUMFLEX u"\ndef",
     "Cut after combining characters does not elide characters."},
    {u"abc" MAN_EMOJI u"def", 3, 5, u"abc\n" MAN_EMOJI u"def",
     "Cut before four-byte emoji does not elide emoji."},
    {u"abc" MAN_EMOJI u"def", 5, 3, u"abc" MAN_EMOJI u"\ndef",
     "Cut after four-byte emoji does not elide emoji."},
    {u"abc" MAN_EMOJI MAN_EMOJI MAN_EMOJI u"def", 3, 3,
     ELLIPSIZE(u"abc", u"def"), "Cut around multiple emoji."},
    {u"abc" MAN_EMOJI MAN_EMOJI MAN_EMOJI u"def", 5, 3,
     ELLIPSIZE(u"abc" MAN_EMOJI, u"def"),
     "Cut the end of a sequence of emoji."},
    {u"abc" MAN_EMOJI MAN_EMOJI MAN_EMOJI u"def", 5, 5,
     ELLIPSIZE(u"abc" MAN_EMOJI, MAN_EMOJI u"def"),
     "Cut the middle of a sequence of emoji."},
    {u"abc" MEDICAL_SYMBOL_EMOJI u"def", 3, 5,
     u"abc\n" MEDICAL_SYMBOL_EMOJI u"def",
     "Cut before two-character emoji does not elide emoji."},
    {u"abc" MEDICAL_SYMBOL_EMOJI u"def", 5, 3,
     u"abc" MEDICAL_SYMBOL_EMOJI u"\ndef",
     "Cut after two-character emoji does not elide emoji."},
    {u"abc" MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER u"def", 3, 7,
     u"abc\n" MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER u"def",
     "Cut before modified emoji does not elide emoji."},
    {u"abc" MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER u"def", 7, 3,
     u"abc" MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER u"\ndef",
     "Cut after modified emoji does not elide emoji."},
    {u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"def", 3, 10,
     u"abc\n" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"def",
     "Cut before compound emoji does not elide emoji."},
    {u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"def", 10, 3,
     u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"\ndef",
     "Cut after compound emoji does not elide emoji."},
    {u"abco" COMBINING_CIRCUMFLEX u" def", 3, 4, ELLIPSIZE(u"abc", u"def"),
     "Eliminates whitespace after elided combining character."},
    {u"abc.png (123" MULT_SYMBOL u"456)", 15, 15,
     BIDIFY(u"abc.png ", u"(123" MULT_SYMBOL u"456)"),
     "Prefer to break before image dimensions (1)."},
    {u"abc.png (123" MULT_SYMBOL u"456)", 8, 15,
     BIDIFY(u"abc.png ", u"(123" MULT_SYMBOL u"456)"),
     "Prefer to break before image dimensions (2)."},
    {u"abc.png (123,456)", 15, 15, BIDIFY(u"abc", u".png (123,456)"),
     "Prefer to break before extension if dimensions malformatted."},
    {u"abc.png (123" MULT_SYMBOL u"456)", 15, 9,
     BIDIFY(u"abc.png ", u"(123" MULT_SYMBOL u"456)"),
     "Force break before image dimensions."},
    {u"abc.png (123" MULT_SYMBOL u"456)", 7, 15,
     BIDIFY(u"abc", u".png (123" MULT_SYMBOL u"456)"),
     "Force break before extension."},
};

class TabHoverCardBubbleViewFilenameEliderElideImplTest
    : public TabHoverCardBubbleViewFilenameEliderTest,
      public testing::TestWithParam<ElideImplTestParams> {};

INSTANTIATE_TEST_SUITE_P(,
                         TabHoverCardBubbleViewFilenameEliderElideImplTest,
                         testing::ValuesIn(kElideImplTestParams));

TEST_P(TabHoverCardBubbleViewFilenameEliderElideImplTest, ElideImpl) {
  const ElideImplTestParams& params = GetParam();
  EXPECT_EQ(std::u16string(params.expected),
            ElideImpl(params.text, params.max_first_line_length,
                      params.max_second_line_length));
}

struct ElideTestParams {
  const char16_t* const text;
  const int chars_per_line;
  const size_t expected_first_line_length;
  const size_t expected_second_line_length;
  const char16_t* const elided;
  const char* const comment;
};

void PrintTo(const ElideTestParams& params, ::std::ostream* os) {
  *os << params.comment << " (\"" << params.text << "\", "
      << params.chars_per_line << ")";
}

const ElideTestParams kElideTestParams[]{
    {u"", 1, 0, 0, u"", "Empty string results in zero-length lines."},
    {u"abc", 3, 3, 3, u"abc",
     "Length equal to width gives full length for each line."},
    {u"abc", 4, 3, 3, u"abc",
     "Length less than width gives full length for each line."},
    {u"abcde", 3, 3, 2, u"abc\nde",
     "Maximum length of lines without ellipsis results in perfect match."},
    {u"abcdef", 3, 3, 2, ELLIPSIZE(u"abc", u"ef"),
     "Length equal to width still leaves space for ellipsis on second line."},
    {u"abcdefg", 3, 3, 2, ELLIPSIZE(u"abc", u"fg"),
     "Length much greater than width still leaves space for ellipsis on second "
     "line."},
    {u"abc", 0, 0, 0, u"", "No available width results in zero length."},
    {u"abco" COMBINING_CIRCUMFLEX u"efg", 4, 5, 3,
     "abco" COMBINING_CIRCUMFLEX u"\nefg",
     "First line ends with combining character."},
    {u"abco" COMBINING_CIRCUMFLEX u"efg", 3, 3, 2, ELLIPSIZE(u"abc", u"fg"),
     "Combining character fully elided between lines."},
    {u"abc" MAN_EMOJI u"efg", 4, 5, 3, u"abc" MAN_EMOJI u"\nefg",
     "First line ends with four-byte emoji."},
    {u"abc" MAN_EMOJI u"efg", 3, 3, 2, ELLIPSIZE(u"abc", u"fg"),
     "Four-byte emoji fully elided between lines."},
    {u"abc" MAN_EMOJI MAN_EMOJI MAN_EMOJI u"efg", 3, 3, 2,
     ELLIPSIZE(u"abc", u"fg"), "Elide around a sequence of emoji."},
    {u"abc" MAN_EMOJI MAN_EMOJI MAN_EMOJI u"efg", 4, 5, 3,
     ELLIPSIZE(u"abc" MAN_EMOJI, u"efg"), "Elide around a sequence of emoji."},
    {u"abc" MAN_EMOJI MAN_EMOJI MAN_EMOJI u"efg", 5, 7, 5,
     u"abc" MAN_EMOJI MAN_EMOJI u"\n" MAN_EMOJI u"efg",
     "Line break in middle of sequence."},
    {u"abc" MEDICAL_SYMBOL_EMOJI u"efg", 4, 5, 3,
     u"abc" MEDICAL_SYMBOL_EMOJI u"\nefg",
     "First line ends with two-character emoji."},
    {u"abc" MEDICAL_SYMBOL_EMOJI u"efg", 3, 3, 2, ELLIPSIZE(u"abc", u"fg"),
     "Two-character emoji fully elided between lines."},
    {u"abc" MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER u"efg", 4, 7, 3,
     u"abc" MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER u"\nefg",
     "First line ends with modified emoji."},
    {u"abc" MAN_EMOJI MEDIUM_SKIN_TONE_MODIFIER u"efg", 3, 3, 2,
     ELLIPSIZE(u"abc", u"fg"), "Modified emoji fully elided between lines."},
    {u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"efg", 4, 10, 3,
     u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"\nefg",
     "First line ends with joined emoji, full string returned."},
    {u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"efgh", 4, 10, 3,
     ELLIPSIZE(u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE, u"fgh"),
     "First line ends with joined emoji, string cut in middle (1)."},
    {u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"defghi", 5, 11, 4,
     ELLIPSIZE(u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"d", u"fghi"),
     "First line ends with joined emoji, string cut in middle (2)."},
    {u"abc" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"efg", 3, 3, 2,
     ELLIPSIZE(u"abc", u"fg"), "Joined emoji fully elided between lines."},
    {u"abcde" MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE
         MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"fg",
     4, 4, 9, ELLIPSIZE(u"abcd", MALE_HEALTH_WORKER_MEDIUM_SKIN_TONE u"fg"),
     "Joined emoji in sequence; first emoji is elided but not second."},
    // These test the combined function of the Elide() method, including the
    // intelligent overlapping and positioning of lines and extensions.
    {u"abcdef", 5, 5, 4, u"abcde\nf", "Wrap at last possible location."},
    {u"abcde.fgh", 6, 6, 5, BIDIFY(u"abcde", u".fgh"),
     "Entire extension placed on second line (1)."},
    {u"abcde.fgh", 5, 5, 4, BIDIFY(u"abcde", u".fgh"),
     "Entire extension placed on second line (2)."},
    {u"abc.fgh", 4, 4, 3, u"abc.\nfgh", "Force break after period."},
    {u"ab.c (1" MULT_SYMBOL u"2)", 9, 9, 8,
     BIDIFY(u"ab.c ", u"(1" MULT_SYMBOL u"2)"),
     "Prefer to break before dimensions."},
    {u"a.b (1" MULT_SYMBOL u"2)", 8, 8, 7,
     BIDIFY(u"a.b ", u"(1" MULT_SYMBOL u"2)"),
     "Force break before dimensions."},
    {u"abcdefghij.png (1" MULT_SYMBOL u"2)", 13, 13, 12,
     BIDIFY(u"abcdefghij", u".png (1" MULT_SYMBOL u"2)"),
     "Force break before extension."},
};

class TabHoverCardBubbleViewFilenameEliderGetLineLengthsTest
    : public TabHoverCardBubbleViewFilenameEliderTest,
      public testing::TestWithParam<ElideTestParams> {};

INSTANTIATE_TEST_SUITE_P(,
                         TabHoverCardBubbleViewFilenameEliderGetLineLengthsTest,
                         testing::ValuesIn(kElideTestParams));

TEST_P(TabHoverCardBubbleViewFilenameEliderGetLineLengthsTest, GetLineLengths) {
  const ElideTestParams& params = GetParam();
  auto result = GetLineLengths(params.text, params.chars_per_line);
  EXPECT_EQ(params.expected_first_line_length, result.first)
      << "Text length is " << std::u16string(params.text).length();
  EXPECT_EQ(params.expected_second_line_length, result.second);
}

TEST_P(TabHoverCardBubbleViewFilenameEliderGetLineLengthsTest, Elide) {
  const ElideTestParams& params = GetParam();
  FilenameElider elider(CreateRenderText(std::u16string()));
  EXPECT_EQ(std::u16string(params.elided),
            elider.Elide(params.text, GetTextRect(params.chars_per_line)));
}

struct FindImageDimensionsTestParams {
  const char16_t* const text;
  const std::u16string::size_type expected;
  const char* const comment;
};

void PrintTo(const FindImageDimensionsTestParams& params, ::std::ostream* os) {
  *os << params.comment << " (\"" << params.text << "\")";
}

const FindImageDimensionsTestParams kFindImageDimensionsTestParams[]{
    {u"", std::u16string::npos, "Empty string has no dimensions."},
    {u"(", std::u16string::npos, "Single open paren has no dimensions."},
    {u"a (", std::u16string::npos, "Single open paren has no dimensions. (2)"},
    {u"a ()", std::u16string::npos, "Just parens has no dimensions."},
    {u"a (" MULT_SYMBOL u")", std::u16string::npos,
     "No numbers has no dimensions."},
    {u"a (1" MULT_SYMBOL u")", std::u16string::npos,
     "Missing height has no dimensions (1)."},
    {u"a (1234" MULT_SYMBOL u")", std::u16string::npos,
     "Missing height has no dimensions (2)."},
    {u"a (" MULT_SYMBOL u"1)", std::u16string::npos,
     "Missing width has no dimensions (1)."},
    {u"a (" MULT_SYMBOL u"1234)", std::u16string::npos,
     "Missing width has no dimensions (2)."},
    {u"a(1" MULT_SYMBOL u"1)", std::u16string::npos,
     "Missing whitespace has no dimensions."},
    {u"a (1" MULT_SYMBOL u"1", std::u16string::npos,
     "Missing end paren has dimensions."},
    {u"a (1234)", std::u16string::npos, "Missing x has no dimensions."},
    {u"a (1" MULT_SYMBOL u"4)", 2U, "Single digits finds dimensions."},
    {u"a (123" MULT_SYMBOL u"456)", 2U, "Multiple digits finds dimensions."},
    {u"a (123 " MULT_SYMBOL u"456)", std::u16string::npos,
     "Extra whitespace has no dimensions (1)."},
    {u"a (123" MULT_SYMBOL u" 456)", std::u16string::npos,
     "Extra whitespace has no dimensions (2)."},
    {u"a (123 " MULT_SYMBOL u" 456)", std::u16string::npos,
     "Extra whitespace has no dimensions (3)."},
    {u"a (1234567890" MULT_SYMBOL u"1234567890)", 2U,
     "All digits finds dimensions."},
    {u"abc def ghi 12345 () xxxxx foo (123" MULT_SYMBOL u"456)", 31U,
     "Long filename finds dimensions."},
    {u"a (123" MULT_SYMBOL u"456) ", std::u16string::npos,
     "Extra space at end has no dimensions."},
    {u"a (123" MULT_SYMBOL u"456)x", std::u16string::npos,
     "Extra characters at end has no dimensions (1)."},
    {u"a (123" MULT_SYMBOL u"456)(", std::u16string::npos,
     "Extra characters at end has no dimensions (2)."},
};

class TabHoverCardBubbleViewFilenameEliderFindImageDimensionsTest
    : public TabHoverCardBubbleViewFilenameEliderTest,
      public testing::TestWithParam<FindImageDimensionsTestParams> {};

INSTANTIATE_TEST_SUITE_P(
    ,
    TabHoverCardBubbleViewFilenameEliderFindImageDimensionsTest,
    testing::ValuesIn(kFindImageDimensionsTestParams));

TEST_P(TabHoverCardBubbleViewFilenameEliderFindImageDimensionsTest,
       FindImageDimensions) {
  const FindImageDimensionsTestParams& params = GetParam();
  EXPECT_EQ(params.expected, FilenameElider::FindImageDimensions(params.text));
}