text: // Copyright 2010-2021, Google Inc.
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are
  // met:
  //
  //     * Redistributions of source code must retain the above copyright
  // notice, this list of conditions and the following disclaimer.
  //     * Redistributions in binary form must reproduce the above
  // copyright notice, this list of conditions and the following disclaimer
  // in the documentation and/or other materials provided with the
  // distribution.
  //     * Neither the name of Google Inc. nor the names of its
  // contributors may be used to endorse or promote products derived from
  // this software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  #include "testing/base/public/googletest.h"
  #include "testing/base/public/gunit.h"
  
  #include "base/logging.h"
  #include "base/util.h"
  #include "protocol/commands.pb.h"
  #include "win32/ime/ime_composition_string.h"
  
  namespace mozc {
  namespace win32 {
  namespace {
  constexpr size_t kNumCandidates = 13;
  const char *kValueList[kNumCandidates] = {
      "Beta", "ベータ", "BETA",   "beta",  "β",          "Β",    "㌼",
      "Beta", "べーた", "ベータ", "be-ta", "ｂｅ－ｔａ", "ﾍﾞｰﾀ",
  };
  const int32 kValueLengths[kNumCandidates] = {
      4, 3, 4, 4, 1, 1, 1, 4, 3, 3, 5, 5, 4,
  };
  const int32 kIDs[kNumCandidates] = {
      0, 1, 2, 3, 4, 5, 6, 7, -1, -2, -3, -7, -11,
  };
  
  std::string GetStringImpl(const CompositionString &composition,
                            const DWORD offset, const DWORD length) {
    const BYTE *addr = reinterpret_cast<const BYTE *>(&composition);
    const wchar_t *string_start =
        reinterpret_cast<const wchar_t *>(addr + offset);
    const std::wstring wstr(string_start, string_start + length);
    std::string str;
    Util::WideToUTF8(wstr.c_str(), &str);
    return str;
  }
  
  BYTE GetAttributeImpl(const CompositionString &composition, const DWORD offset,
                        const DWORD length, size_t index) {
    const BYTE *addr = reinterpret_cast<const BYTE *>(&composition);
    const BYTE *attribute_start = addr + offset;
    EXPECT_LE(0, index);
    EXPECT_LT(index, length);
    return attribute_start[index];
  }
  
  #define GET_STRING(composition, field_name)                               \
    GetStringImpl((composition), (composition).info.dw##field_name##Offset, \
                  (composition).info.dw##field_name##Len)
  
  #define GET_ATTRIBUTE(composition, field_name, index)                        \
    GetAttributeImpl((composition), (composition).info.dw##field_name##Offset, \
                     (composition).info.dw##field_name##Len, (index))
  
  // TODO(yukawa): Make a common library for this function.
  void FillOutputForSuggestion(commands::Output *output) {
    DCHECK_NE(nullptr, output);
    output->Clear();
  
    output->set_mode(commands::HIRAGANA);
    output->set_consumed(true);
    {
      commands::Preedit *preedit = output->mutable_preedit();
      preedit->set_cursor(4);
      {
        commands::Preedit::Segment *segment = preedit->add_segment();
        segment->set_annotation(commands::Preedit::Segment::UNDERLINE);
        segment->set_value("あるふぁ");
        segment->set_value_length(4);
        segment->set_key("あるふぁ");
      }
    }
    {
      commands::Candidates *candidates = output->mutable_candidates();
      candidates->set_size(2);
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(0);
        candidate->set_value("AlphaBeta");
        candidate->set_id(0);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(1);
        candidate->set_value("アルファ");
        candidate->set_id(1);
      }
      candidates->set_position(0);
      candidates->set_category(commands::SUGGESTION);
      candidates->set_display_type(commands::MAIN);
      {
        commands::Footer *footer = candidates->mutable_footer();
        footer->set_sub_label("build 436");
      }
    }
    {
      commands::Status *status = output->mutable_status();
      status->set_activated(true);
      status->set_mode(commands::HIRAGANA);
    }
    {
      commands::CandidateList *candidate_list =
          output->mutable_all_candidate_words();
      candidate_list->set_focused_index(0);
      {
        commands::CandidateWord *candidate = candidate_list->add_candidates();
        candidate->set_id(0);
        candidate->set_index(0);
        candidate->set_key("あるふぁべーた");
        candidate->set_value("AlphaBeta");
      }
      {
        commands::CandidateWord *candidate = candidate_list->add_candidates();
        candidate->set_id(1);
        candidate->set_index(1);
        candidate->set_value("アルファ");
      }
      candidate_list->set_category(commands::SUGGESTION);
    }
  }
  
  // TODO(yukawa): Make a common library for this function.
  void FillOutputForPrediction(commands::Output *output) {
    DCHECK_NE(nullptr, output);
    output->Clear();
  
    output->set_mode(commands::HIRAGANA);
    output->set_consumed(true);
    {
      commands::Preedit *preedit = output->mutable_preedit();
      preedit->set_cursor(9);
      {
        commands::Preedit::Segment *segment = preedit->add_segment();
        segment->set_annotation(commands::Preedit::Segment::HIGHLIGHT);
        segment->set_value("AlphaBeta");
        segment->set_value_length(9);
        segment->set_key("あるふぁ");
      }
    }
    {
      commands::Candidates *candidates = output->mutable_candidates();
      candidates->set_focused_index(0);
      candidates->set_size(2);
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(0);
        candidate->set_value("AlphaBeta");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_shortcut("1");
        }
        candidate->set_id(0);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(1);
        candidate->set_value("アルファ");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_shortcut("2");
        }
        candidate->set_id(1);
      }
      candidates->set_position(0);
      candidates->set_category(commands::PREDICTION);
      candidates->set_display_type(commands::MAIN);
      {
        commands::Footer *footer = candidates->mutable_footer();
        footer->set_index_visible(true);
        footer->set_logo_visible(true);
        footer->set_sub_label("build 436");
      }
    }
    {
      commands::Status *status = output->mutable_status();
      status->set_activated(true);
      status->set_mode(commands::HIRAGANA);
    }
    {
      commands::CandidateList *candidate_list =
          output->mutable_all_candidate_words();
      candidate_list->set_focused_index(0);
      {
        commands::CandidateWord *candidate = candidate_list->add_candidates();
        candidate->set_id(0);
        candidate->set_index(0);
        candidate->set_key("あるふぁべーた");
        candidate->set_value("AlphaBeta");
      }
      {
        commands::CandidateWord *candidate = candidate_list->add_candidates();
        candidate->set_id(1);
        candidate->set_index(1);
        candidate->set_value("アルファ");
      }
      candidate_list->set_category(commands::PREDICTION);
    }
  }
  
  // TODO(yukawa): Make a common library for this function.
  void FillOutputForConversion(commands::Output *output, int focused_index,
                               bool has_candidates) {
    DCHECK_LE(0, focused_index);
    DCHECK_GT(kNumCandidates, focused_index);
    DCHECK_NE(nullptr, output);
    output->Clear();
  
    const int32 focused_value_length = kValueLengths[focused_index];
    const char *focused_value = kValueList[focused_index];
    const int32 focused_id = kIDs[focused_index];
  
    output->set_mode(commands::HIRAGANA);
    output->set_consumed(true);
    {
      const int alpha_length = 5;
      commands::Preedit *preedit = output->mutable_preedit();
      preedit->set_cursor(alpha_length + focused_value_length);
      {
        commands::Preedit::Segment *segment = preedit->add_segment();
        segment->set_annotation(commands::Preedit::Segment::UNDERLINE);
        segment->set_value("Alpha");
        segment->set_value_length(alpha_length);
        segment->set_key("あるふぁ");
      }
      {
        commands::Preedit::Segment *segment = preedit->add_segment();
        segment->set_annotation(commands::Preedit::Segment::HIGHLIGHT);
        segment->set_value(focused_value);
        segment->set_value_length(focused_value_length);
        segment->set_key("べーた");
      }
      preedit->set_highlighted_position(alpha_length);
    }
  
    if (has_candidates) {
      commands::Candidates *candidates = output->mutable_candidates();
      candidates->set_focused_index(focused_index);
  
      candidates->set_size(9);
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(0);
        candidate->set_value("BETA");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("[半] アルファベット");
          annotation->set_shortcut("1");
        }
        candidate->set_id(0);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(1);
        candidate->set_value("ベータ");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("[全] カタカナ");
          annotation->set_shortcut("2");
        }
        candidate->set_id(1);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(2);
        candidate->set_value("beta");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("[半] アルファベット");
          annotation->set_shortcut("3");
        }
        candidate->set_id(2);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(3);
        candidate->set_value("β");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("ギリシャ文字(小文字)");
          annotation->set_shortcut("4");
        }
        candidate->set_id(3);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(4);
        candidate->set_value("Β");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("ギリシャ文字(大文字)");
          annotation->set_shortcut("5");
        }
        candidate->set_id(4);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(5);
        candidate->set_value("㌼");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("<機種依存文字>");
          annotation->set_shortcut("6");
        }
        candidate->set_id(5);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(6);
        candidate->set_value("Beta");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("[半] アルファベット");
          annotation->set_shortcut("7");
        }
        candidate->set_id(6);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(7);
        candidate->set_value("べーた");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_description("ひらがな");
          annotation->set_shortcut("8");
        }
        candidate->set_id(7);
      }
      {
        commands::Candidates::Candidate *candidate = candidates->add_candidate();
        candidate->set_index(8);
        candidate->set_value("そのほかの文字種");
        {
          commands::Annotation *annotation = candidate->mutable_annotation();
          annotation->set_shortcut("9");
        }
        candidate->set_id(-3);
      }
      candidates->set_position(5);
      {
        commands::Candidates *sub_candidates =
            candidates->mutable_subcandidates();
        sub_candidates->set_focused_index(2);
        sub_candidates->set_size(5);
        {
          {
            commands::Candidates::Candidate *candidate =
                sub_candidates->add_candidate();
            candidate->set_index(0);
            candidate->set_value("べーた");
            {
              commands::Annotation *annotation = candidate->mutable_annotation();
              annotation->set_description("ひらがな");
            }
            candidate->set_id(-1);
          }
          {
            commands::Candidates::Candidate *candidate =
                sub_candidates->add_candidate();
            candidate->set_index(1);
            candidate->set_value("ベータ");
            {
              commands::Annotation *annotation = candidate->mutable_annotation();
              annotation->set_description("[全] カタカナ");
            }
            candidate->set_id(-2);
          }
          {
            commands::Candidates::Candidate *candidate =
                sub_candidates->add_candidate();
            candidate->set_index(2);
            candidate->set_value("be-ta");
            {
              commands::Annotation *annotation = candidate->mutable_annotation();
              annotation->set_description("[半]");
            }
            candidate->set_id(-3);
          }
          {
            commands::Candidates::Candidate *candidate =
                sub_candidates->add_candidate();
            candidate->set_index(3);
            candidate->set_value("ｂｅ－ｔａ");
            {
              commands::Annotation *annotation = candidate->mutable_annotation();
              annotation->set_description("[全]");
            }
            candidate->set_id(-7);
          }
          {
            commands::Candidates::Candidate *candidate =
                sub_candidates->add_candidate();
            candidate->set_index(4);
            candidate->set_value("ﾍﾞｰﾀ");
            {
              commands::Annotation *annotation = candidate->mutable_annotation();
              annotation->set_description("[半] カタカナ");
            }
            candidate->set_id(-11);
          }
        }
        sub_candidates->set_position(8);
        sub_candidates->set_category(commands::TRANSLITERATION);
        sub_candidates->set_display_type(commands::CASCADE);
      }
      candidates->set_category(commands::CONVERSION);
      candidates->set_display_type(commands::MAIN);
      {
        commands::Footer *footer = candidates->mutable_footer();
        footer->set_index_visible(true);
        footer->set_logo_visible(true);
        footer->set_sub_label("build 436");
      }
    }
    {
      commands::Status *status = output->mutable_status();
      status->set_activated(true);
      status->set_mode(commands::HIRAGANA);
    }
  
    {
      commands::CandidateList *candidate_list =
          output->mutable_all_candidate_words();
      candidate_list->set_focused_index(focused_index);
  
      for (size_t i = 0; i < kNumCandidates; ++i) {
        commands::CandidateWord *candidate = candidate_list->add_candidates();
        candidate->set_id(kIDs[i]);
        candidate->set_index(i);
        candidate->set_value(kValueList[i]);
      }
      candidate_list->set_category(commands::CONVERSION);
    }
  }
  }  // namespace
  
  TEST(ImeCompositionStringTest, StartCompositionTest) {
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
  
    commands::Output output;
    commands::Preedit *preedit = output.mutable_preedit();
  
    commands::Preedit::Segment *segment = preedit->add_segment();
    preedit->set_cursor(1);
  
    segment->set_annotation(commands::Preedit::Segment::UNDERLINE);
    segment->set_key("が");
    segment->set_value("が");
    segment->set_value_length(1);
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
    EXPECT_EQ(2, messages.size());
  
    EXPECT_EQ(WM_IME_STARTCOMPOSITION, messages[0].message());
    EXPECT_EQ(0, messages[0].wparam());
    EXPECT_EQ(0, messages[0].lparam());
  
    EXPECT_EQ(WM_IME_COMPOSITION, messages[1].message());
    EXPECT_EQ(0, messages[1].wparam());
    // When the preedit is updated, the following flags should be sent
    // regardless of which field is actually updated. Otherwise, some
    // applications such as wordpad OOo Writer 3.0 will not update composition
    // window and caret state properly.
    EXPECT_EQ(
        (GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE | GCS_COMPSTR |
         GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART),
        messages[1].lparam());
  
    EXPECT_EQ(0, compstr.focused_character_index_);
    EXPECT_EQ(2, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(2, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(1, compstr.info.dwCompAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompClauseLen);
    EXPECT_EQ(1, compstr.info.dwCompStrLen);
    EXPECT_EQ(1, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(0, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(0, compstr.info.dwResultClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultStrLen);
    EXPECT_EQ("ｶﾞ", GET_STRING(compstr, CompReadStr));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 0));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 1));
  
    // TODO(yukawa): Check other fields in |compstr.info|
  }
  
  TEST(ImeCompositionStringTest, EndCompositionWhenCompositionBecomesEmpty) {
    // WM_IME_COMPOSITION should be sent with setting 0 to wparam and lparam.
    // Otherwise, OOo 3.0.1 will not show the caret again.
  
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
    compstr.info.dwCompStrLen = 1;
    compstr.info.dwCompReadStrLen = 1;
  
    commands::Output output;
    output.set_consumed(true);
    output.set_mode(mozc::commands::HIRAGANA);
    output.mutable_status()->set_activated(true);
    output.mutable_status()->set_mode(mozc::commands::HIRAGANA);
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
    EXPECT_EQ(2, messages.size());
  
    EXPECT_EQ(WM_IME_COMPOSITION, messages[0].message());
    EXPECT_EQ(0, messages[0].wparam());
    EXPECT_EQ(0, messages[0].lparam());
  
    EXPECT_EQ(WM_IME_ENDCOMPOSITION, messages[1].message());
    EXPECT_EQ(0, messages[1].wparam());
    EXPECT_EQ(0, messages[1].lparam());
  
    EXPECT_EQ(0, compstr.focused_character_index_);
    EXPECT_EQ(0, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(0, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(0, compstr.info.dwCompAttrLen);
    EXPECT_EQ(0, compstr.info.dwCompClauseLen);
    EXPECT_EQ(0, compstr.info.dwCompStrLen);
    EXPECT_EQ(-1, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(0, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(0, compstr.info.dwResultClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultStrLen);
  }
  
  TEST(ImeCompositionStringTest, EndCompositionWhenCompositionIsCommitted) {
    // WM_IME_COMPOSITION should be sent up to once.
    // Otherwise, the result string will be committed twice in wordpad.exe.
  
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
    compstr.info.dwCompStrLen = 1;
    compstr.info.dwCompReadStrLen = 1;
  
    commands::Output output;
    output.set_consumed(true);
    output.set_mode(mozc::commands::HIRAGANA);
    output.mutable_status()->set_activated(true);
    output.mutable_status()->set_mode(mozc::commands::HIRAGANA);
  
    commands::Result *result = output.mutable_result();
    result->set_key("が");
    result->set_value("が");
    result->set_type(mozc::commands::Result::STRING);
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
    EXPECT_EQ(2, messages.size());
  
    EXPECT_EQ(WM_IME_COMPOSITION, messages[0].message());
    EXPECT_EQ(0, messages[0].wparam());
    // When the result string is updated, the following flags should be sent
    // regardless of which field is actually updated. Otherwise, some
    // applications such as wordpad OOo Writer 3.0 will not update composition
    // window and caret state properly.
    EXPECT_EQ((GCS_RESULTREADSTR | GCS_RESULTREADCLAUSE | GCS_RESULTSTR |
               GCS_RESULTCLAUSE),
              messages[0].lparam());
  
    EXPECT_EQ(WM_IME_ENDCOMPOSITION, messages[1].message());
    EXPECT_EQ(0, messages[1].wparam());
    EXPECT_EQ(0, messages[1].lparam());
  
    EXPECT_EQ(0, compstr.focused_character_index_);
    EXPECT_EQ(0, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(0, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(0, compstr.info.dwCompAttrLen);
    EXPECT_EQ(0, compstr.info.dwCompClauseLen);
    EXPECT_EQ(0, compstr.info.dwCompStrLen);
    EXPECT_EQ(-1, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(8, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(2, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(8, compstr.info.dwResultClauseLen);
    EXPECT_EQ(1, compstr.info.dwResultStrLen);
    EXPECT_EQ("ｶﾞ", GET_STRING(compstr, ResultReadStr));
  }
  
  TEST(ImeCompositionStringTest, SpaceKeyWhenIMEIsTurnedOn_Issue3200585) {
    // If the current state is no composition and the server returns a result
    // string, it should be interpreted as one-shot composition as follows.
    //   WM_IME_STARTCOMPOSITION
    //     -> WM_IME_COMPOSITION
    //        -> WM_IME_ENDCOMPOSITION
    // Otherwise, you cannot input Full-width space on Outlook 2010. (b/3200585)
  
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
  
    commands::Output output;
    // Emulate special_key::SPACE key when IME is turned on.
    output.set_mode(mozc::commands::HIRAGANA);
    output.set_consumed(true);
    output.mutable_result()->set_key(" ");
    output.mutable_result()->set_value("　");
    output.mutable_result()->set_type(mozc::commands::Result::STRING);
    output.mutable_status()->set_activated(true);
    output.mutable_status()->set_mode(mozc::commands::HIRAGANA);
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
    EXPECT_EQ(3, messages.size());
  
    EXPECT_EQ(WM_IME_STARTCOMPOSITION, messages[0].message());
    EXPECT_EQ(0, messages[0].wparam());
    EXPECT_EQ(0, messages[0].lparam());
  
    EXPECT_EQ(WM_IME_COMPOSITION, messages[1].message());
    EXPECT_EQ(0, messages[1].wparam());
    EXPECT_EQ((GCS_RESULTREADSTR | GCS_RESULTREADCLAUSE | GCS_RESULTSTR |
               GCS_RESULTCLAUSE),
              messages[1].lparam());
  
    EXPECT_EQ(WM_IME_ENDCOMPOSITION, messages[2].message());
    EXPECT_EQ(0, messages[2].wparam());
    EXPECT_EQ(0, messages[2].lparam());
  
    EXPECT_EQ(0, compstr.focused_character_index_);
    EXPECT_EQ(0, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(0, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(0, compstr.info.dwCompAttrLen);
    EXPECT_EQ(0, compstr.info.dwCompClauseLen);
    EXPECT_EQ(0, compstr.info.dwCompStrLen);
    EXPECT_EQ(-1, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(8, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(1, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(8, compstr.info.dwResultClauseLen);
    EXPECT_EQ(1, compstr.info.dwResultStrLen);
    EXPECT_EQ("　", GET_STRING(compstr, ResultStr));
    EXPECT_EQ(" ", GET_STRING(compstr, ResultReadStr));
  }
  
  TEST(ImeCompositionStringTest,
       EndCompositionWhenCompositionIsCommittedWithPreedit) {
    // WM_IME_COMPOSITION should be sent up to once.
    // Otherwise, the result string will be committed twice in wordpad.exe.
  
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
    compstr.info.dwCompStrLen = 1;
    compstr.info.dwCompReadStrLen = 1;
  
    commands::Output output;
    output.set_consumed(true);
    output.set_mode(mozc::commands::HIRAGANA);
    output.mutable_status()->set_activated(true);
    output.mutable_status()->set_mode(mozc::commands::HIRAGANA);
  
    commands::Result *result = output.mutable_result();
    result->set_key("が");
    result->set_value("が");
    result->set_type(mozc::commands::Result::STRING);
  
    commands::Preedit *preedit = output.mutable_preedit();
    preedit->set_cursor(1);
  
    commands::Preedit::Segment *segment = preedit->add_segment();
    segment->set_annotation(commands::Preedit::Segment::UNDERLINE);
    segment->set_key("が");
    segment->set_value("が");
    segment->set_value_length(1);
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
    EXPECT_EQ(1, messages.size());
  
    EXPECT_EQ(WM_IME_COMPOSITION, messages[0].message());
    EXPECT_EQ(0, messages[0].wparam());
    // When both the preedit and result string is updated, the following flags
    // should be sent regardless of which field is actually updated. Otherwise,
    // some applications such as wordpad OOo Writer 3.0 will not update
    // composition window and caret state properly.
    EXPECT_EQ(
        (GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE | GCS_COMPSTR |
         GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART) |
            (GCS_RESULTREADSTR | GCS_RESULTREADCLAUSE | GCS_RESULTSTR |
             GCS_RESULTCLAUSE),
        messages[0].lparam());
  
    EXPECT_EQ(0, compstr.focused_character_index_);
    EXPECT_EQ(2, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(2, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(1, compstr.info.dwCompAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompClauseLen);
    EXPECT_EQ(1, compstr.info.dwCompStrLen);
    EXPECT_EQ(1, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(8, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(2, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(8, compstr.info.dwResultClauseLen);
    EXPECT_EQ(1, compstr.info.dwResultStrLen);
    EXPECT_EQ("ｶﾞ", GET_STRING(compstr, CompReadStr));
    EXPECT_EQ("ｶﾞ", GET_STRING(compstr, ResultReadStr));
  
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 0));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 1));
  }
  
  TEST(ImeCompositionStringTest, Suggest) {
    commands::Output output;
    FillOutputForSuggestion(&output);
  
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
    EXPECT_EQ(2, messages.size());
  
    EXPECT_EQ(WM_IME_STARTCOMPOSITION, messages[0].message());
    EXPECT_EQ(0, messages[0].wparam());
    EXPECT_EQ(0, messages[0].lparam());
  
    EXPECT_EQ(WM_IME_COMPOSITION, messages[1].message());
    EXPECT_EQ(0, messages[1].wparam());
    // When the preedit is updated, the following flags should be sent
    // regardless of which field is actually updated. Otherwise, some
    // applications such as wordpad OOo Writer 3.0 will not update composition
    // window and caret state properly.
    EXPECT_EQ(
        (GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE | GCS_COMPSTR |
         GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART),
        messages[1].lparam());
  
    EXPECT_EQ(0, compstr.focused_character_index_);
    EXPECT_EQ(4, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(4, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(4, compstr.info.dwCompAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompClauseLen);
    EXPECT_EQ(4, compstr.info.dwCompStrLen);
    EXPECT_EQ(4, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(0, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(0, compstr.info.dwResultClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultStrLen);
  
    EXPECT_EQ("ｱﾙﾌｧ", GET_STRING(compstr, CompReadStr));
    EXPECT_EQ("あるふぁ", GET_STRING(compstr, CompStr));
  
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 0));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 1));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 2));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompReadAttr, 3));
  
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompAttr, 0));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompAttr, 1));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompAttr, 2));
    EXPECT_EQ(ATTR_INPUT, GET_ATTRIBUTE(compstr, CompAttr, 3));
  }
  
  TEST(ImeCompositionStringTest, Predict) {
    commands::Output output;
    FillOutputForPrediction(&output);
  
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
    EXPECT_EQ(2, messages.size());
  
    EXPECT_EQ(WM_IME_STARTCOMPOSITION, messages[0].message());
    EXPECT_EQ(0, messages[0].wparam());
    EXPECT_EQ(0, messages[0].lparam());
  
    EXPECT_EQ(WM_IME_COMPOSITION, messages[1].message());
    EXPECT_EQ(0, messages[1].wparam());
    // When the preedit is updated, the following flags should be sent
    // regardless of which field is actually updated. Otherwise, some
    // applications such as wordpad OOo Writer 3.0 will not update composition
    // window and caret state properly.
    EXPECT_EQ(
        (GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE | GCS_COMPSTR |
         GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART),
        messages[1].lparam());
  
    EXPECT_EQ(0, compstr.focused_character_index_);
    EXPECT_EQ(4, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(4, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(9, compstr.info.dwCompAttrLen);
    EXPECT_EQ(8, compstr.info.dwCompClauseLen);
    EXPECT_EQ(9, compstr.info.dwCompStrLen);
    EXPECT_EQ(9, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(0, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(0, compstr.info.dwResultClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultStrLen);
  
    EXPECT_EQ("ｱﾙﾌｧ", GET_STRING(compstr, CompReadStr));
    EXPECT_EQ("AlphaBeta", GET_STRING(compstr, CompStr));
  
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 0));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 1));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 2));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 3));
  
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 0));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 1));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 2));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 3));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 4));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 5));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 6));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 7));
    EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 8));
  }
  
  TEST(ImeCompositionStringTest, Convert) {
    commands::Output output;
  
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
  
    // First conversion
    // It is common for traditional IMEs not to display candidate window for the
    // first conversion.  These IMEs do not fill CandidateInfo for the first
    // conversiontoo, too.  See b/2978825 for details.  Mozc server conforms
    // to this behavior by keeping |output.candidates()| empty for the first
    // conversion.
    FillOutputForConversion(&output, 0, false);
    {
      std::vector<UIMessage> messages;
      EXPECT_TRUE(compstr.Update(output, &messages));
      EXPECT_EQ(2, messages.size());
  
      EXPECT_EQ(WM_IME_STARTCOMPOSITION, messages[0].message());
      EXPECT_EQ(0, messages[0].wparam());
      EXPECT_EQ(0, messages[0].lparam());
  
      EXPECT_EQ(WM_IME_COMPOSITION, messages[1].message());
      EXPECT_EQ(0, messages[1].wparam());
      // When the preedit is updated, the following flags should be sent
      // regardless of which field is actually updated. Otherwise, some
      // applications such as wordpad OOo Writer 3.0 will not update composition
      // window and caret state properly.
      EXPECT_EQ(
          (GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE | GCS_COMPSTR |
           GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART),
          messages[1].lparam());
  
      EXPECT_EQ(5, compstr.focused_character_index_);
      EXPECT_EQ(8, compstr.info.dwCompReadAttrLen);
      EXPECT_EQ(12, compstr.info.dwCompReadClauseLen);
      EXPECT_EQ(8, compstr.info.dwCompReadStrLen);
      EXPECT_EQ(9, compstr.info.dwCompAttrLen);
      EXPECT_EQ(12, compstr.info.dwCompClauseLen);
      EXPECT_EQ(9, compstr.info.dwCompStrLen);
      EXPECT_EQ(5, compstr.info.dwCursorPos);
      EXPECT_EQ(0, compstr.info.dwDeltaStart);
      EXPECT_EQ(0, compstr.info.dwResultReadClauseLen);
      EXPECT_EQ(0, compstr.info.dwResultReadStrLen);
      EXPECT_EQ(0, compstr.info.dwResultClauseLen);
      EXPECT_EQ(0, compstr.info.dwResultStrLen);
  
      EXPECT_EQ("ｱﾙﾌｧﾍﾞｰﾀ", GET_STRING(compstr, CompReadStr));
      EXPECT_EQ("AlphaBeta", GET_STRING(compstr, CompStr));
  
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 0));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 1));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 2));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 3));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 4));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 5));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 6));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 7));
  
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 0));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 1));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 2));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 3));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 4));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 5));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 6));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 7));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 8));
    }
  
    // Second conversion
    FillOutputForConversion(&output, 1, true);
    {
      std::vector<UIMessage> messages;
      EXPECT_TRUE(compstr.Update(output, &messages));
      EXPECT_EQ(1, messages.size());
  
      EXPECT_EQ(WM_IME_COMPOSITION, messages[0].message());
      EXPECT_EQ(0, messages[0].wparam());
      // When the preedit is updated, the following flags should be sent
      // regardless of which field is actually updated. Otherwise, some
      // applications such as wordpad OOo Writer 3.0 will not update composition
      // window and caret state properly.
      EXPECT_EQ(
          (GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE | GCS_COMPSTR |
           GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART),
          messages[0].lparam());
  
      EXPECT_EQ(5, compstr.focused_character_index_);
      EXPECT_EQ(8, compstr.info.dwCompReadAttrLen);
      EXPECT_EQ(12, compstr.info.dwCompReadClauseLen);
      EXPECT_EQ(8, compstr.info.dwCompReadStrLen);
      EXPECT_EQ(8, compstr.info.dwCompAttrLen);
      EXPECT_EQ(12, compstr.info.dwCompClauseLen);
      EXPECT_EQ(8, compstr.info.dwCompStrLen);
      EXPECT_EQ(5, compstr.info.dwCursorPos);
      EXPECT_EQ(0, compstr.info.dwDeltaStart);
      EXPECT_EQ(0, compstr.info.dwResultReadClauseLen);
      EXPECT_EQ(0, compstr.info.dwResultReadStrLen);
      EXPECT_EQ(0, compstr.info.dwResultClauseLen);
      EXPECT_EQ(0, compstr.info.dwResultStrLen);
  
      EXPECT_EQ("ｱﾙﾌｧﾍﾞｰﾀ", GET_STRING(compstr, CompReadStr));
      EXPECT_EQ("Alphaベータ", GET_STRING(compstr, CompStr));
  
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 0));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 1));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 2));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 3));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 4));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 5));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 6));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 7));
  
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 0));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 1));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 2));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 3));
      EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 4));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 5));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 6));
      EXPECT_EQ(ATTR_TARGET_CONVERTED, GET_ATTRIBUTE(compstr, CompAttr, 7));
    }
  }
  
  // Check if surrogate-pair is handled properly. See b/4163234 and b/4159275
  // for details.
  TEST(ImeCompositionStringTest, SurrogatePairSupport) {
    CompositionString compstr;
    EXPECT_TRUE(compstr.Initialize());
  
    commands::Output output;
    output.set_mode(commands::HIRAGANA);
    output.set_consumed(true);
    {
      commands::Preedit *preedit = output.mutable_preedit();
      preedit->set_cursor(5);
      {
        commands::Preedit::Segment *segment = preedit->add_segment();
        segment->set_annotation(commands::Preedit::Segment::UNDERLINE);
        segment->set_value("𠮟る");
        segment->set_value_length(2);
        segment->set_key("しかる");
      }
      {
        commands::Preedit::Segment *segment = preedit->add_segment();
        segment->set_annotation(commands::Preedit::Segment::UNDERLINE);
        segment->set_value("と");
        segment->set_value_length(1);
        segment->set_key("と");
      }
      {
        commands::Preedit::Segment *segment = preedit->add_segment();
        segment->set_annotation(commands::Preedit::Segment::HIGHLIGHT);
        segment->set_value("叱る");
        segment->set_value_length(2);
        segment->set_key("しかる");
      }
      preedit->set_highlighted_position(3);
    }
    {
      commands::Status *status = output.mutable_status();
      status->set_activated(true);
      status->set_mode(commands::HIRAGANA);
    }
  
    std::vector<UIMessage> messages;
    EXPECT_TRUE(compstr.Update(output, &messages));
  
    // Here, |focused_character_index_| != Preedit::highlighted_position()
    // because of surrogate parir.
    EXPECT_EQ(4, compstr.focused_character_index_);
    EXPECT_EQ(7, compstr.info.dwCompReadAttrLen);
    EXPECT_EQ(16, compstr.info.dwCompReadClauseLen);
    EXPECT_EQ(7, compstr.info.dwCompReadStrLen);
    EXPECT_EQ(6, compstr.info.dwCompAttrLen);
    EXPECT_EQ(16, compstr.info.dwCompClauseLen);
    EXPECT_EQ(6, compstr.info.dwCompStrLen);
    EXPECT_EQ(4, compstr.info.dwCursorPos);
    EXPECT_EQ(0, compstr.info.dwDeltaStart);
    EXPECT_EQ(0, compstr.info.dwResultReadClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultReadStrLen);
    EXPECT_EQ(0, compstr.info.dwResultClauseLen);
    EXPECT_EQ(0, compstr.info.dwResultStrLen);
  
    EXPECT_EQ("ｼｶﾙﾄｼｶﾙ", GET_STRING(compstr, CompReadStr));
  
    EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 0));
    EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 1));
    EXPECT_EQ(ATTR_CONVERTED, GET_ATTRIBUTE(compstr, CompReadAttr, 2));
  
    // TODO(yukawa): Check other fields in |compstr.info|
  }
  }  // namespace win32
  }  // namespace mozc
  