text: // Copyright 2017 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 <string>
  
  #include "third_party/blink/renderer/platform/text/text_boundaries.h"
  
  #include "testing/gtest/include/gtest/gtest.h"
  #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
  #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
  
  namespace blink {
  
  class TextBoundariesTest : public testing::Test {};
  
  namespace {
  
  std::pair<String, int> ParsePositionMarker(const std::string input8) {
    String input16 = String::FromUTF8(input8.data(), input8.size());
    input16.Ensure16Bit();
    const size_t position = input16.find('|');
    DCHECK(position != kNotFound) << input8 << " should have position marker(|).";
    String output = input16.Left(position) + input16.Substring(position + 1);
    output.Ensure16Bit();
    return {output, position};
  }
  
  std::string MakeResultText(const String& text, int start, int end) {
    StringBuilder builder;
    if (start < 0 && end < 0) {
      builder.Append(text);
    } else if (start < 0) {
      builder.Append(text.Left(end));
      builder.Append('^');
      builder.Append(text.Substring(end));
    } else if (end < 0) {
      builder.Append(text.Left(start));
      builder.Append('|');
      builder.Append(text.Substring(start));
    } else {
      builder.Append(text.Left(start));
      builder.Append('^');
      builder.Append(text.Substring(start, end - start));
      builder.Append('|');
    }
    builder.Append(text.Substring(end));
    return builder.ToString().Utf8();
  }
  
  // Returns word boundray with start(^) and end(|) markes from text with
  // position(|) marker.
  std::string TryFindWordBackward(const std::string input8) {
    std::pair<String, int> string_and_offset = ParsePositionMarker(input8);
    const String text16 = string_and_offset.first;
    const int position = string_and_offset.second;
    std::pair<int, int> start_and_end =
        FindWordBackward(text16.Characters16(), text16.length(), position);
    return MakeResultText(text16, start_and_end.first, start_and_end.second);
  }
  
  // Returns word boundray with start(^) and end(|) markes from text with
  // position(|) marker.
  std::string TryFindWordForward(const std::string input8) {
    std::pair<String, int> string_and_offset = ParsePositionMarker(input8);
    const String text16 = string_and_offset.first;
    const int position = string_and_offset.second;
    std::pair<int, int> start_and_end =
        FindWordForward(text16.Characters16(), text16.length(), position);
    return MakeResultText(text16, start_and_end.first, start_and_end.second);
  }
  
  }  // namespace
  
  TEST_F(TextBoundariesTest, BackwardBasic) {
    EXPECT_EQ("^|abc def", TryFindWordBackward("|abc def"));
    EXPECT_EQ("^abc| def", TryFindWordBackward("a|bc def"));
    EXPECT_EQ("^abc| def", TryFindWordBackward("ab|c def"));
    EXPECT_EQ("^abc| def", TryFindWordBackward("abc| def"));
    EXPECT_EQ("abc^ |def", TryFindWordBackward("abc |def"));
    EXPECT_EQ("abc ^def|", TryFindWordBackward("abc d|ef"));
    EXPECT_EQ("abc ^def|", TryFindWordBackward("abc de|f"));
    EXPECT_EQ("abc ^def|", TryFindWordBackward("abc def|"));
  }
  
  TEST_F(TextBoundariesTest, ForwardBasic) {
    EXPECT_EQ("^abc| def", TryFindWordForward("|abc def"));
    EXPECT_EQ("^abc| def", TryFindWordForward("a|bc def"));
    EXPECT_EQ("^abc| def", TryFindWordForward("ab|c def"));
    EXPECT_EQ("abc^ |def", TryFindWordForward("abc| def"));
    EXPECT_EQ("abc ^def|", TryFindWordForward("abc |def"));
    EXPECT_EQ("abc ^def|", TryFindWordForward("abc d|ef"));
    EXPECT_EQ("abc ^def|", TryFindWordForward("abc de|f"));
    EXPECT_EQ("abc def^|", TryFindWordForward("abc def|"));
  }
  
  TEST_F(TextBoundariesTest, ForwardBiDi) {
    EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625",
              TryFindWordForward(u8"|\u0620\u0621\u0622 \u0623\u0624\u0625"));
    EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625",
              TryFindWordForward(u8"\u0620|\u0621\u0622 \u0623\u0624\u0625"));
    EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625",
              TryFindWordForward(u8"\u0620\u0621|\u0622 \u0623\u0624\u0625"));
    EXPECT_EQ(u8"\u0620\u0621\u0622^ |\u0623\u0624\u0625",
              TryFindWordForward(u8"\u0620\u0621\u0622| \u0623\u0624\u0625"));
    EXPECT_EQ(u8"\u0620\u0621\u0622 ^\u0623\u0624\u0625|",
              TryFindWordForward(u8"\u0620\u0621\u0622 |\u0623\u0624\u0625"));
    EXPECT_EQ(u8"\u0620\u0621\u0622 \u0623\u0624\u0625^|",
              TryFindWordForward(u8"\u0620\u0621\u0622 \u0623\u0624\u0625|"));
  }
  
  TEST_F(TextBoundariesTest, ForwardBiDiMixed) {
    EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
              TryFindWordForward(u8"|abc\u0620\u0621\u0622"));
    EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
              TryFindWordForward(u8"ab|c\u0620\u0621\u0622"));
    EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
              TryFindWordForward(u8"abc|\u0620\u0621\u0622"))
        << "At L1/L2 boundary";
    EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
              TryFindWordForward(u8"abc\u0620|\u0621\u0622"));
  
    EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
              TryFindWordForward(u8"|\u0620\u0621\u0622xyz"));
    EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
              TryFindWordForward(u8"\u0620|\u0621\u0622xyz"));
    EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
              TryFindWordForward(u8"\u0620\u0621\u0622|xyz"))
        << "At L2/L1 boundary";
    EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
              TryFindWordForward(u8"\u0620\u0621\u0622xy|z"));
  }
  
  TEST_F(TextBoundariesTest, ForwardOne) {
    EXPECT_EQ("^a|", TryFindWordForward("|a"));
    EXPECT_EQ("a^|", TryFindWordForward("a|")) << "No word after |";
  }
  
  TEST_F(TextBoundariesTest, ForwardParenthesis) {
    EXPECT_EQ("^(|abc)", TryFindWordForward("|(abc)"));
    EXPECT_EQ("(^abc|)", TryFindWordForward("(|abc)"));
    EXPECT_EQ("(^abc|)", TryFindWordForward("(a|bc)"));
    EXPECT_EQ("(^abc|)", TryFindWordForward("(ab|c)"));
    EXPECT_EQ("(abc)^|", TryFindWordForward("(abc)|")) << "No word after |";
  }
  
  TEST_F(TextBoundariesTest, ForwardPunctuations) {
    EXPECT_EQ("^abc|,,", TryFindWordForward("|abc,,"));
    EXPECT_EQ("abc^,|,", TryFindWordForward("abc|,,"));
  }
  
  // A sequence of whitespaces is treated as a single word.
  // See http://unicode.org/reports/tr29/#WB3d .
  TEST_F(TextBoundariesTest, ForwardWhitespaces) {
    EXPECT_EQ("^  |abc  def  ", TryFindWordForward("|  abc  def  "));
    EXPECT_EQ("^  |abc  def  ", TryFindWordForward(" | abc  def  "));
    EXPECT_EQ("  ^abc|  def  ", TryFindWordForward("  |abc  def  "));
    EXPECT_EQ("  ^abc|  def  ", TryFindWordForward("  a|bc  def  "));
    EXPECT_EQ("  ^abc|  def  ", TryFindWordForward("  ab|c  def  "));
    EXPECT_EQ("  abc^  |def  ", TryFindWordForward("  abc|  def  "));
    EXPECT_EQ("  abc^  |def  ", TryFindWordForward("  abc | def  "));
    EXPECT_EQ("  abc  ^def|  ", TryFindWordForward("  abc  |def  "));
    EXPECT_EQ("  abc  ^def|  ", TryFindWordForward("  abc  d|ef  "));
    EXPECT_EQ("  abc  ^def|  ", TryFindWordForward("  abc  de|f  "));
    EXPECT_EQ("  abc  def^  |", TryFindWordForward("  abc  def|  "));
    EXPECT_EQ("  abc  def^  |", TryFindWordForward("  abc  def | "));
    EXPECT_EQ("  abc  def  ^|", TryFindWordForward("  abc  def  |"))
        << "No word after |";
  }
  
  TEST_F(TextBoundariesTest, ForwardZero) {
    EXPECT_EQ("^|", TryFindWordForward("|"));
  }
  
  }  // namespace blink
  