blob: 2e4880fb997ed4b4473655fa1096b6844945e0c6 [file] [log] [blame]
// Copyright 2010-2015, 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 "session/internal/session_output.h"
#include <string>
#include "base/port.h"
#include "base/text_normalizer.h"
#include "base/util.h"
#include "converter/segments.h"
#include "session/commands.pb.h"
#include "session/internal/candidate_list.h"
#include "testing/base/public/gunit.h"
namespace mozc {
namespace session {
struct DummySegment {
const char *value;
const int32 usage_id;
const char *usage_title;
const char *usage_description;
};
void FillDummySegment(const DummySegment *dummy_segments, const size_t num,
Segment *segment, CandidateList *candidate_list) {
for (size_t i = 0; i < num; ++i) {
Segment::Candidate *cand = segment->push_back_candidate();
candidate_list->AddCandidate(i, dummy_segments[i].value);
cand->value = dummy_segments[i].value;
cand->usage_id = dummy_segments[i].usage_id;
cand->usage_title = dummy_segments[i].usage_title;
cand->usage_description = dummy_segments[i].usage_description;
}
}
TEST(SessionOutputTest, FillCandidate) {
Segment segment;
Candidate candidate;
CandidateList candidate_list(true);
commands::Candidates_Candidate candidate_proto;
const string kValue13 = "Value only";
const string kValue42 = "The answer";
const string kPrefix42 = "prefix";
const string kSuffix42 = "suffix";
const string kDescription42 = "description";
const string kSubcandidateList = "Subcandidates";
// Make 100 candidates
for (size_t i = 0; i < 100; ++i) {
segment.push_back_candidate();
}
segment.mutable_candidate(13)->value = kValue13;
segment.mutable_candidate(42)->value = kValue42;
segment.mutable_candidate(42)->prefix = kPrefix42;
segment.mutable_candidate(42)->suffix = kSuffix42;
segment.mutable_candidate(42)->description = kDescription42;
candidate_list.set_name(kSubcandidateList);
static const int kFirstIdInSubList = -123;
candidate_list.AddCandidate(kFirstIdInSubList, "minus 123");
candidate_list.AddCandidate(-456, "minus 456");
candidate_list.AddCandidate(-789, "minus 789");
candidate.set_id(13);
SessionOutput::FillCandidate(segment, candidate, &candidate_proto);
EXPECT_EQ(13, candidate_proto.id());
EXPECT_EQ(kValue13, candidate_proto.value());
EXPECT_FALSE(candidate_proto.has_annotation());
candidate.Clear();
candidate_proto.Clear();
candidate.set_id(42);
SessionOutput::FillCandidate(segment, candidate, &candidate_proto);
EXPECT_EQ(42, candidate_proto.id());
EXPECT_EQ(kValue42, candidate_proto.value());
EXPECT_TRUE(candidate_proto.has_annotation());
EXPECT_EQ(kPrefix42, candidate_proto.annotation().prefix());
EXPECT_EQ(kSuffix42, candidate_proto.annotation().suffix());
EXPECT_EQ(kDescription42, candidate_proto.annotation().description());
candidate.Clear();
candidate_proto.Clear();
candidate.set_subcandidate_list(&candidate_list);
SessionOutput::FillCandidate(segment, candidate, &candidate_proto);
EXPECT_TRUE(candidate_proto.has_id());
EXPECT_EQ(kFirstIdInSubList, candidate_proto.id());
EXPECT_EQ(kSubcandidateList, candidate_proto.value());
EXPECT_FALSE(candidate_proto.has_annotation());
}
TEST(SessionOutputTest, FillCandidates) {
Segment segment;
Candidate candidate;
CandidateList candidate_list(true);
CandidateList subcandidate_list(true);
commands::Candidates candidates_proto;
const string kSubcandidateList = "Subcandidates";
const char* kValues[5] = {"0", "1", "2:sub0", "3:sub1", "4:sub2"};
// Make 5 candidates
for (size_t i = 0; i < 5; ++i) {
segment.push_back_candidate()->value = kValues[i];
}
candidate_list.set_focused(true);
candidate_list.set_page_size(9);
candidate_list.AddCandidate(0, "0");
candidate_list.AddCandidate(1, "1");
candidate_list.AddSubCandidateList(&subcandidate_list);
subcandidate_list.set_focused(true);
subcandidate_list.set_name(kSubcandidateList);
subcandidate_list.AddCandidate(2, "2");
subcandidate_list.AddCandidate(3, "3");
subcandidate_list.AddCandidate(4, "4");
// Focused index = 0. page_size = 9.
SessionOutput::FillCandidates(segment, candidate_list, 0, &candidates_proto);
EXPECT_EQ(9, candidates_proto.page_size());
EXPECT_EQ(3, candidates_proto.candidate_size());
EXPECT_EQ(0, candidates_proto.position());
EXPECT_TRUE(candidates_proto.has_focused_index());
EXPECT_EQ(0, candidates_proto.focused_index());
EXPECT_EQ(kValues[0], candidates_proto.candidate(0).value());
EXPECT_EQ(kValues[1], candidates_proto.candidate(1).value());
EXPECT_EQ(kSubcandidateList, candidates_proto.candidate(2).value());
EXPECT_FALSE(candidates_proto.has_subcandidates());
// Focused index = 2 with a subcandidate list. page_size = 5.
candidates_proto.Clear();
candidate_list.MoveToId(3);
candidate_list.set_page_size(5);
SessionOutput::FillCandidates(segment, candidate_list, 1, &candidates_proto);
EXPECT_EQ(5, candidates_proto.page_size());
EXPECT_EQ(3, candidates_proto.candidate_size());
EXPECT_EQ(1, candidates_proto.position());
EXPECT_TRUE(candidates_proto.has_focused_index());
EXPECT_EQ(2, candidates_proto.focused_index());
EXPECT_EQ(kValues[0], candidates_proto.candidate(0).value());
EXPECT_EQ(kValues[1], candidates_proto.candidate(1).value());
EXPECT_EQ(kSubcandidateList, candidates_proto.candidate(2).value());
EXPECT_EQ(0, candidates_proto.candidate(0).index());
EXPECT_EQ(1, candidates_proto.candidate(1).index());
EXPECT_EQ(2, candidates_proto.candidate(2).index());
// Check the values of the subcandidate list.
EXPECT_TRUE(candidates_proto.has_subcandidates());
EXPECT_EQ(3, candidates_proto.subcandidates().candidate_size());
EXPECT_EQ(2, candidates_proto.subcandidates().position());
EXPECT_TRUE(candidates_proto.subcandidates().has_focused_index());
EXPECT_EQ(1, candidates_proto.subcandidates().focused_index());
EXPECT_EQ(kValues[2], candidates_proto.subcandidates().candidate(0).value());
EXPECT_EQ(kValues[3], candidates_proto.subcandidates().candidate(1).value());
EXPECT_EQ(kValues[4], candidates_proto.subcandidates().candidate(2).value());
// Check focused_index.
candidates_proto.Clear();
candidate_list.set_focused(false);
subcandidate_list.set_focused(true);
SessionOutput::FillCandidates(segment, candidate_list, 0, &candidates_proto);
EXPECT_FALSE(candidates_proto.has_focused_index());
EXPECT_TRUE(candidates_proto.subcandidates().has_focused_index());
candidates_proto.Clear();
candidate_list.set_focused(false);
subcandidate_list.set_focused(false);
SessionOutput::FillCandidates(segment, candidate_list, 0, &candidates_proto);
EXPECT_FALSE(candidates_proto.has_focused_index());
EXPECT_FALSE(candidates_proto.subcandidates().has_focused_index());
candidates_proto.Clear();
candidate_list.set_focused(true);
subcandidate_list.set_focused(false);
SessionOutput::FillCandidates(segment, candidate_list, 0, &candidates_proto);
EXPECT_TRUE(candidates_proto.has_focused_index());
EXPECT_FALSE(candidates_proto.subcandidates().has_focused_index());
}
TEST(SessionOutputTest, FillAllCandidateWords) {
// IDs are ordered by BFS.
//
// ID|Idx| Candidate list tree
// 1| 0 | [1:[sub1_1,
// 5| 1 | sub1_2:[subsub1_1,
// 6| 2 | subsub1_2],
// 2| 3 | sub1_3],
// 0| 4 | 2,
// 3| 5 | 3:[sub2_1,
// 4| 6 | sub2_2]]
CandidateList main_list(true);
CandidateList sub1(true);
CandidateList sub2(true);
CandidateList subsub1(true);
commands::CandidateList candidates_proto;
// Initialize Segment
Segment segment;
const char* kNormalKey = "key";
segment.set_key(kNormalKey);
const char* kDescription = "desc";
const char* kValues[7] =
{"2", "sub1_1", "sub1_3", "sub2_1", "sub2_2", "subsub1_1", "subsub1_2"};
const size_t kValueSize = arraysize(kValues);
for (size_t i = 0; i < kValueSize; ++i) {
Segment::Candidate *candidate = segment.push_back_candidate();
candidate->content_key = kNormalKey;
candidate->value = kValues[i];
candidate->description = kDescription;
}
// Set special key to ID:4 / Index:6
const char* kSpecialKey = "Special Key";
segment.mutable_candidate(4)->content_key = kSpecialKey;
// Main
main_list.AddSubCandidateList(&sub1);
main_list.AddCandidate(0, kValues[0]);
main_list.AddSubCandidateList(&sub2);
// Sub1
sub1.AddCandidate(1, kValues[1]);
sub1.AddSubCandidateList(&subsub1);
sub1.AddCandidate(2, kValues[2]);
// Sub2
sub2.AddCandidate(3, kValues[3]);
sub2.AddCandidate(4, kValues[4]);
// SubSub1
subsub1.AddCandidate(5, kValues[5]);
subsub1.AddCandidate(6, kValues[6]);
// Set forcus to ID:5 / Index:1
main_list.set_focused(true);
sub1.set_focused(true);
subsub1.set_focused(true);
main_list.MoveToId(5);
EXPECT_EQ(5, main_list.focused_id());
EXPECT_EQ(0, main_list.focused_index());
EXPECT_EQ(1, sub1.focused_index());
EXPECT_EQ(0, subsub1.focused_index());
// End of Initialization
// Exexcute FillAllCandidateWords
const commands::Category kCategory = commands::PREDICTION;
SessionOutput::FillAllCandidateWords(segment, main_list, kCategory,
&candidates_proto);
// Varidation
EXPECT_EQ(1, candidates_proto.focused_index());
EXPECT_EQ(kCategory, candidates_proto.category());
EXPECT_EQ(kValueSize, candidates_proto.candidates_size());
EXPECT_EQ(1, candidates_proto.candidates(0).id());
EXPECT_EQ(5, candidates_proto.candidates(1).id());
EXPECT_EQ(6, candidates_proto.candidates(2).id());
EXPECT_EQ(2, candidates_proto.candidates(3).id());
EXPECT_EQ(0, candidates_proto.candidates(4).id());
EXPECT_EQ(3, candidates_proto.candidates(5).id());
EXPECT_EQ(4, candidates_proto.candidates(6).id());
EXPECT_EQ(0, candidates_proto.candidates(0).index());
EXPECT_EQ(1, candidates_proto.candidates(1).index());
EXPECT_EQ(2, candidates_proto.candidates(2).index());
EXPECT_EQ(3, candidates_proto.candidates(3).index());
EXPECT_EQ(4, candidates_proto.candidates(4).index());
EXPECT_EQ(5, candidates_proto.candidates(5).index());
EXPECT_EQ(6, candidates_proto.candidates(6).index());
EXPECT_FALSE(candidates_proto.candidates(0).has_key());
EXPECT_FALSE(candidates_proto.candidates(1).has_key());
EXPECT_FALSE(candidates_proto.candidates(2).has_key());
EXPECT_FALSE(candidates_proto.candidates(3).has_key());
EXPECT_FALSE(candidates_proto.candidates(4).has_key());
EXPECT_FALSE(candidates_proto.candidates(5).has_key());
EXPECT_TRUE(candidates_proto.candidates(6).has_key());
EXPECT_EQ(kSpecialKey, candidates_proto.candidates(6).key());
EXPECT_EQ(kValues[1], candidates_proto.candidates(0).value());
EXPECT_EQ(kValues[5], candidates_proto.candidates(1).value());
EXPECT_EQ(kValues[6], candidates_proto.candidates(2).value());
EXPECT_EQ(kValues[2], candidates_proto.candidates(3).value());
EXPECT_EQ(kValues[0], candidates_proto.candidates(4).value());
EXPECT_EQ(kValues[3], candidates_proto.candidates(5).value());
EXPECT_EQ(kValues[4], candidates_proto.candidates(6).value());
EXPECT_TRUE(candidates_proto.candidates(0).has_annotation());
EXPECT_TRUE(candidates_proto.candidates(1).has_annotation());
EXPECT_TRUE(candidates_proto.candidates(2).has_annotation());
EXPECT_TRUE(candidates_proto.candidates(3).has_annotation());
EXPECT_TRUE(candidates_proto.candidates(4).has_annotation());
EXPECT_TRUE(candidates_proto.candidates(5).has_annotation());
EXPECT_TRUE(candidates_proto.candidates(6).has_annotation());
}
TEST(SessionOutputTest, ShouldShowUsages) {
{
Segment segment;
CandidateList candidate_list(true);
CandidateList sub(true);
static const DummySegment dummy_segments[] = {
{ "val0", 0, "", "" },
{ "val1", 0, "", "" },
{ "val2", 0, "", "" },
{ "val3", 0, "", "" },
{ "val4", 0, "", "" },
};
FillDummySegment(dummy_segments, 5, &segment, &candidate_list);
candidate_list.AddSubCandidateList(&sub);
candidate_list.set_focused(true);
ASSERT_TRUE(candidate_list.MoveToId(0));
ASSERT_FALSE(SessionOutput::ShouldShowUsages(segment, candidate_list));
}
{
Segment segment;
CandidateList candidate_list(true);
CandidateList sub(true);
static const DummySegment dummy_segments[] = {
{ "val0", 0, "", "" },
{ "val1", 10, "title1", "" },
{ "val2", 0, "", "" },
{ "val3", 0, "", "" },
{ "val4", 0, "", "" },
};
FillDummySegment(dummy_segments, 5, &segment, &candidate_list);
candidate_list.AddSubCandidateList(&sub);
candidate_list.set_focused(true);
ASSERT_TRUE(candidate_list.MoveToId(0));
ASSERT_TRUE(SessionOutput::ShouldShowUsages(segment, candidate_list));
}
{
Segment segment;
CandidateList candidate_list(true);
CandidateList sub(true);
static const DummySegment dummy_segments[] = {
{ "val00", 10, "title00", "" },
{ "val01", 0, "", "" },
{ "val02", 0, "", "" },
{ "val03", 0, "", "" },
{ "val04", 0, "", "" },
{ "val05", 0, "", "" },
{ "val06", 0, "", "" },
{ "val07", 0, "", "" },
{ "val08", 0, "", "" },
{ "val09", 0, "", "" },
{ "val10", 20, "title10", "" },
{ "val11", 0, "", "" },
{ "val12", 0, "", "" },
{ "val13", 30, "title13", "" },
{ "val14", 0, "", "" },
{ "val15", 0, "", "" },
{ "val16", 0, "", "" },
{ "val17", 0, "", "" },
{ "val18", 0, "", "" },
{ "val19", 0, "", "" },
{ "val20", 0, "", "" },
{ "val21", 0, "", "" },
{ "val22", 0, "", "" },
{ "val23", 0, "", "" },
{ "val24", 0, "", "" },
{ "val25", 0, "", "" },
{ "val26", 0, "", "" },
{ "val27", 0, "", "" },
{ "val28", 0, "", "" },
{ "val29", 0, "", "" },
};
FillDummySegment(dummy_segments, 30, &segment, &candidate_list);
candidate_list.AddSubCandidateList(&sub);
// pages of candidate_list:
// [00-08],[09-17],[18-26],[27-29]+subcandidate
candidate_list.set_focused(true);
ASSERT_TRUE(candidate_list.MoveToId(0));
ASSERT_TRUE(SessionOutput::ShouldShowUsages(segment, candidate_list));
ASSERT_TRUE(candidate_list.MoveToId(8));
ASSERT_TRUE(SessionOutput::ShouldShowUsages(segment, candidate_list));
ASSERT_TRUE(candidate_list.MoveToId(9));
ASSERT_TRUE(SessionOutput::ShouldShowUsages(segment, candidate_list));
ASSERT_TRUE(candidate_list.MoveToId(17));
ASSERT_TRUE(SessionOutput::ShouldShowUsages(segment, candidate_list));
ASSERT_TRUE(candidate_list.MoveToId(18));
ASSERT_FALSE(SessionOutput::ShouldShowUsages(segment, candidate_list));
ASSERT_TRUE(candidate_list.MoveToId(26));
ASSERT_FALSE(SessionOutput::ShouldShowUsages(segment, candidate_list));
ASSERT_TRUE(candidate_list.MoveToId(27));
ASSERT_FALSE(SessionOutput::ShouldShowUsages(segment, candidate_list));
}
}
TEST(SessionOutputTest, FillUsages) {
Segment segment;
CandidateList candidate_list(true);
CandidateList sub(true);
commands::Candidates candidates_proto;
static const DummySegment dummy_segments[] = {
{ "val00", 10, "title00", "desc00" },
{ "val01", 0, "", "" },
{ "val02", 0, "", "" },
{ "val03", 0, "", "" },
{ "val04", 20, "title04", "desc04" },
{ "val05", 0, "", "" },
{ "val06", 0, "", "" },
{ "val07", 0, "", "" },
{ "val08", 0, "", "" },
{ "val09", 0, "", "" },
{ "val10", 30, "title10", "desc10" },
{ "val11", 40, "title11", "desc11" },
{ "val12", 50, "title12", "desc12" },
{ "val13", 60, "title13", "desc13" },
{ "val14", 0, "", "" },
{ "val15", 0, "", "" },
{ "val16", 0, "", "" },
{ "val17", 0, "", "" },
{ "val18", 0, "", "" },
{ "val19", 100, "title100", "desc100" },
{ "val20", 110, "title110", "desc110" },
{ "val21", 100, "title100", "desc100" },
{ "val22", 110, "title110", "desc110" },
{ "val23", 0, "", "" },
{ "val24", 0, "", "" },
{ "val25", 0, "", "" },
{ "val26", 0, "", "" },
{ "val27", 0, "", "" },
{ "val28", 0, "", "" },
{ "val29", 0, "", "" },
};
FillDummySegment(dummy_segments, 30, &segment, &candidate_list);
candidate_list.AddSubCandidateList(&sub);
// pages of candidate_list:
// [00-08],[09-17],[18-26],[27-29]+subcandidate
candidate_list.set_focused(true);
candidate_list.MoveToId(2);
candidates_proto.Clear();
SessionOutput::FillUsages(segment, candidate_list, &candidates_proto);
ASSERT_TRUE(candidates_proto.has_usages());
// There is no focused usage.
EXPECT_FALSE(candidates_proto.usages().has_focused_index());
EXPECT_EQ(2, candidates_proto.usages().information_size());
EXPECT_EQ(10, candidates_proto.usages().information(0).id());
EXPECT_EQ(dummy_segments[0].usage_title,
candidates_proto.usages().information(0).title());
EXPECT_EQ(dummy_segments[0].usage_description,
candidates_proto.usages().information(0).description());
EXPECT_EQ(20, candidates_proto.usages().information(1).id());
EXPECT_EQ(dummy_segments[4].usage_title,
candidates_proto.usages().information(1).title());
EXPECT_EQ(dummy_segments[4].usage_description,
candidates_proto.usages().information(1).description());
candidate_list.MoveToId(12);
candidates_proto.Clear();
SessionOutput::FillUsages(segment, candidate_list, &candidates_proto);
ASSERT_TRUE(candidates_proto.has_usages());
// Focused usage index is 20
EXPECT_TRUE(candidates_proto.usages().has_focused_index());
EXPECT_EQ(2, candidates_proto.usages().focused_index());
EXPECT_EQ(4, candidates_proto.usages().information_size());
EXPECT_EQ(30, candidates_proto.usages().information(0).id());
EXPECT_EQ(dummy_segments[10].usage_title,
candidates_proto.usages().information(0).title());
EXPECT_EQ(dummy_segments[10].usage_description,
candidates_proto.usages().information(0).description());
EXPECT_EQ(40, candidates_proto.usages().information(1).id());
EXPECT_EQ(dummy_segments[11].usage_title,
candidates_proto.usages().information(1).title());
EXPECT_EQ(dummy_segments[11].usage_description,
candidates_proto.usages().information(1).description());
EXPECT_EQ(50, candidates_proto.usages().information(2).id());
EXPECT_EQ(dummy_segments[12].usage_title,
candidates_proto.usages().information(2).title());
EXPECT_EQ(dummy_segments[12].usage_description,
candidates_proto.usages().information(2).description());
EXPECT_EQ(60, candidates_proto.usages().information(3).id());
EXPECT_EQ(dummy_segments[13].usage_title,
candidates_proto.usages().information(3).title());
EXPECT_EQ(dummy_segments[13].usage_description,
candidates_proto.usages().information(3).description());
candidate_list.MoveToId(19);
candidates_proto.Clear();
SessionOutput::FillUsages(segment, candidate_list, &candidates_proto);
ASSERT_TRUE(candidates_proto.has_usages());
EXPECT_TRUE(candidates_proto.usages().has_focused_index());
EXPECT_EQ(0, candidates_proto.usages().focused_index());
// usages(id:100) of "val19" and "val21" are merged
EXPECT_EQ(2, candidates_proto.usages().information_size());
EXPECT_EQ(100, candidates_proto.usages().information(0).id());
EXPECT_EQ(dummy_segments[19].usage_title,
candidates_proto.usages().information(0).title());
EXPECT_EQ(dummy_segments[19].usage_description,
candidates_proto.usages().information(0).description());
EXPECT_EQ(110, candidates_proto.usages().information(1).id());
EXPECT_EQ(dummy_segments[20].usage_title,
candidates_proto.usages().information(1).title());
EXPECT_EQ(dummy_segments[20].usage_description,
candidates_proto.usages().information(1).description());
candidate_list.MoveToId(20);
candidates_proto.Clear();
SessionOutput::FillUsages(segment, candidate_list, &candidates_proto);
ASSERT_TRUE(candidates_proto.has_usages());
EXPECT_TRUE(candidates_proto.usages().has_focused_index());
EXPECT_EQ(1, candidates_proto.usages().focused_index());
// usages(id:100) of "val19" and "val21" are merged
candidate_list.MoveToId(21);
candidates_proto.Clear();
SessionOutput::FillUsages(segment, candidate_list, &candidates_proto);
ASSERT_TRUE(candidates_proto.has_usages());
EXPECT_TRUE(candidates_proto.usages().has_focused_index());
EXPECT_EQ(0, candidates_proto.usages().focused_index());
// usages(id:110) of "val20" and "val22" are merged
candidate_list.MoveToId(22);
candidates_proto.Clear();
SessionOutput::FillUsages(segment, candidate_list, &candidates_proto);
ASSERT_TRUE(candidates_proto.has_usages());
EXPECT_TRUE(candidates_proto.usages().has_focused_index());
EXPECT_EQ(1, candidates_proto.usages().focused_index());
candidate_list.MoveToId(28);
candidates_proto.Clear();
SessionOutput::FillUsages(segment, candidate_list, &candidates_proto);
ASSERT_FALSE(candidates_proto.has_usages());
}
TEST(SessionOutputTest, FillShortcuts) {
const string kDigits = "123456789";
commands::Candidates candidates_proto1;
for (size_t i = 0; i < 10; ++i) {
candidates_proto1.add_candidate();
}
ASSERT_EQ(10, candidates_proto1.candidate_size());
SessionOutput::FillShortcuts(kDigits, &candidates_proto1);
EXPECT_EQ(kDigits.substr(0, 1),
candidates_proto1.candidate(0).annotation().shortcut());
EXPECT_EQ(kDigits.substr(8, 1),
candidates_proto1.candidate(8).annotation().shortcut());
EXPECT_FALSE(candidates_proto1.candidate(9).annotation().has_shortcut());
commands::Candidates candidates_proto2;
for (size_t i = 0; i < 3; ++i) {
candidates_proto2.add_candidate();
}
ASSERT_EQ(3, candidates_proto2.candidate_size());
SessionOutput::FillShortcuts(kDigits, &candidates_proto2);
EXPECT_EQ(kDigits.substr(0, 1),
candidates_proto2.candidate(0).annotation().shortcut());
EXPECT_EQ(kDigits.substr(2, 1),
candidates_proto2.candidate(2).annotation().shortcut());
}
TEST(SessionOutputTest, FillFooter) {
commands::Candidates candidates;
EXPECT_TRUE(SessionOutput::FillFooter(commands::SUGGESTION, &candidates));
EXPECT_TRUE(candidates.has_footer());
#if defined(CHANNEL_DEV) && defined(GOOGLE_JAPANESE_INPUT_BUILD)
EXPECT_FALSE(candidates.footer().has_label());
EXPECT_TRUE(candidates.footer().has_sub_label());
EXPECT_EQ(0, candidates.footer().sub_label().find("build "));
#else // CHANNEL_DEV && GOOGLE_JAPANESE_INPUT_BUILD
EXPECT_TRUE(candidates.footer().has_label());
EXPECT_FALSE(candidates.footer().has_sub_label());
// "Tabキーで選択"
const char kLabel[] = ("Tab\xE3\x82\xAD\xE3\x83\xBC\xE3\x81\xA7"
"\xE9\x81\xB8\xE6\x8A\x9E");
EXPECT_EQ(kLabel, candidates.footer().label());
#endif // CHANNEL_DEV && GOOGLE_JAPANESE_INPUT_BUILD
EXPECT_FALSE(candidates.footer().index_visible());
EXPECT_FALSE(candidates.footer().logo_visible());
candidates.Clear();
EXPECT_TRUE(SessionOutput::FillFooter(commands::PREDICTION, &candidates));
EXPECT_TRUE(candidates.has_footer());
EXPECT_FALSE(candidates.footer().has_label());
EXPECT_TRUE(candidates.footer().index_visible());
EXPECT_TRUE(candidates.footer().logo_visible());
candidates.Clear();
EXPECT_TRUE(SessionOutput::FillFooter(commands::CONVERSION, &candidates));
EXPECT_TRUE(candidates.has_footer());
EXPECT_FALSE(candidates.footer().has_label());
EXPECT_TRUE(candidates.footer().index_visible());
EXPECT_TRUE(candidates.footer().logo_visible());
candidates.Clear();
EXPECT_FALSE(SessionOutput::FillFooter(commands::TRANSLITERATION,
&candidates));
EXPECT_FALSE(candidates.has_footer());
candidates.Clear();
EXPECT_FALSE(SessionOutput::FillFooter(commands::USAGE, &candidates));
EXPECT_FALSE(candidates.has_footer());
candidates.Clear();
for (int i = 0; i < 20; ++i) {
commands::Candidates::Candidate *c = candidates.add_candidate();
c->set_index(i);
c->set_value("dummy");
c->set_id(i);
// Candidates with even Id can be deleted.
c->mutable_annotation()->set_deletable(i % 2 == 0);
}
for (int i = 0; i < 20; ++i) {
candidates.clear_footer();
candidates.set_focused_index(i);
EXPECT_TRUE(SessionOutput::FillFooter(commands::PREDICTION, &candidates));
if (i % 2 == 0) {
ASSERT_TRUE(candidates.has_footer());
ASSERT_TRUE(candidates.footer().has_label());
#if defined(OS_MACOSX)
const char kDeleteInstruction[] = // "control+fn+deleteで履歴から削除"
"\x63\x6F\x6E\x74\x72\x6F\x6C\x2B\x66\x6E\x2B\x64\x65\x6C\x65"
"\x74\x65\xE3\x81\xA7\xE5\xB1\xA5\xE6\xAD\xB4\xE3\x81\x8B\xE3"
"\x82\x89\xE5\x89\x8A\xE9\x99\xA4";
#elif defined(__native_client__)
const char kDeleteInstruction[] = // "ctrl+alt+backspaceで履歴から削除"
"\x63\x74\x72\x6C\x2B\x61\x6C\x74\x2B\x62\x61\x63\x6B\x73\x70"
"\x61\x63\x65\xE3\x81\xA7\xE5\xB1\xA5\xE6\xAD\xB4\xE3\x81\x8B"
"\xE3\x82\x89\xE5\x89\x8A\xE9\x99\xA4";
#else // !OS_MACOSX && !__native_client__
const char kDeleteInstruction[] = // "Ctrl+Delで履歴から削除"
"\x43\x74\x72\x6C\x2B\x44\x65\x6C\xE3\x81\xA7\xE5\xB1\xA5"
"\xE6\xAD\xB4\xE3\x81\x8B\xE3\x82\x89\xE5\x89\x8A\xE9\x99\xA4";
#endif // OS_MACOSX || __native_client__
EXPECT_EQ(kDeleteInstruction, candidates.footer().label());
#if defined(CHANNEL_DEV) && defined(GOOGLE_JAPANESE_INPUT_BUILD)
} else {
EXPECT_FALSE(candidates.footer().has_label());
EXPECT_TRUE(candidates.footer().has_sub_label());
EXPECT_EQ(0, candidates.footer().sub_label().find("build "));
#endif // CHANNEL_DEV && GOOGLE_JAPANESE_INPUT_BUILD
}
}
}
TEST(SessionOutputTest, FillSubLabel) {
commands::Footer footer;
footer.set_label("to be deleted");
SessionOutput::FillSubLabel(&footer);
EXPECT_TRUE(footer.has_sub_label());
EXPECT_FALSE(footer.has_label());
EXPECT_GT(footer.sub_label().size(), 6); // 6 == strlen("build ")
// sub_label should start with "build ".
EXPECT_EQ(0, footer.sub_label().find("build "));
}
TEST(SessionOutputTest, AddSegment) {
commands::Preedit preedit;
int index = 0;
{
// "ゔ" and "〜" are characters to be processed by
// TextNormalizer::NormalizePreeditText and
// TextNormalizer::NormalizeCandidateText
const string kKey = "ゔ〜 preedit focused";
const string kValue = "ゔ〜 PREEDIT FOCUSED";
const int types = SessionOutput::PREEDIT | SessionOutput::FOCUSED;
EXPECT_TRUE(SessionOutput::AddSegment(kKey, kValue, types, &preedit));
EXPECT_EQ(index + 1, preedit.segment_size());
const commands::Preedit::Segment &segment = preedit.segment(index);
string normalized_key;
TextNormalizer::NormalizePreeditText(kKey, &normalized_key);
EXPECT_EQ(normalized_key, segment.key());
string normalized_value;
TextNormalizer::NormalizePreeditText(kValue, &normalized_value);
EXPECT_EQ(normalized_value, segment.value());
EXPECT_EQ(Util::CharsLen(normalized_value), segment.value_length());
EXPECT_EQ(commands::Preedit::Segment::UNDERLINE, segment.annotation());
++index;
}
{
const string kKey = "ゔ〜 preedit";
const string kValue = "ゔ〜 PREEDIT";
const int types = SessionOutput::PREEDIT;
EXPECT_TRUE(SessionOutput::AddSegment(kKey, kValue, types, &preedit));
EXPECT_EQ(index + 1, preedit.segment_size());
const commands::Preedit::Segment &segment = preedit.segment(index);
string normalized_key;
TextNormalizer::NormalizePreeditText(kKey, &normalized_key);
EXPECT_EQ(normalized_key, segment.key());
string normalized_value;
TextNormalizer::NormalizePreeditText(kValue, &normalized_value);
EXPECT_EQ(normalized_value, segment.value());
EXPECT_EQ(Util::CharsLen(normalized_value), segment.value_length());
EXPECT_EQ(commands::Preedit::Segment::UNDERLINE, segment.annotation());
++index;
}
{
const string kKey = "ゔ〜 conversion focused";
const string kValue = "ゔ〜 CONVERSION FOCUSED";
const int types = SessionOutput::CONVERSION | SessionOutput::FOCUSED;
EXPECT_TRUE(SessionOutput::AddSegment(kKey, kValue, types, &preedit));
EXPECT_EQ(index + 1, preedit.segment_size());
const commands::Preedit::Segment &segment = preedit.segment(index);
string normalized_key;
TextNormalizer::NormalizePreeditText(kKey, &normalized_key);
EXPECT_EQ(normalized_key, segment.key());
// Normalization is performed in Rewriter.
string normalized_value = kValue;
EXPECT_EQ(normalized_value, segment.value());
EXPECT_EQ(Util::CharsLen(normalized_value), segment.value_length());
EXPECT_EQ(commands::Preedit::Segment::HIGHLIGHT, segment.annotation());
++index;
}
{
const string kKey = "ゔ〜 conversion";
const string kValue = "ゔ〜 CONVERSION";
const int types = SessionOutput::CONVERSION;
EXPECT_TRUE(SessionOutput::AddSegment(kKey, kValue, types, &preedit));
EXPECT_EQ(index + 1, preedit.segment_size());
const commands::Preedit::Segment &segment = preedit.segment(index);
string normalized_key;
TextNormalizer::NormalizePreeditText(kKey, &normalized_key);
EXPECT_EQ(normalized_key, segment.key());
// Normalization is performed in Rewriter.
string normalized_value = kValue;
EXPECT_EQ(normalized_value, segment.value());
EXPECT_EQ(Util::CharsLen(normalized_value), segment.value_length());
EXPECT_EQ(commands::Preedit::Segment::UNDERLINE, segment.annotation());
++index;
}
{
const string kKey = "abc";
const string kValue = ""; // empty value
const int types = SessionOutput::CONVERSION;
EXPECT_FALSE(SessionOutput::AddSegment(kKey, kValue, types, &preedit));
EXPECT_EQ(index, preedit.segment_size());
}
}
TEST(SessionOutputTest, FillConversionResultWithoutNormalization) {
// "ゔ"
const char kInput[] = "\xE3\x82\x94";
commands::Result result;
SessionOutput::FillConversionResultWithoutNormalization(
kInput, kInput, &result);
EXPECT_EQ(commands::Result::STRING, result.type());
EXPECT_EQ(kInput, result.key()); // should not be normalized
EXPECT_EQ(kInput, result.value()); // should not be normalized
}
TEST(SessionOutputTest, FillConversionResult) {
{
commands::Result result;
SessionOutput::FillConversionResult("abc", "ABC", &result);
EXPECT_EQ(commands::Result::STRING, result.type());
EXPECT_EQ("abc", result.key());
EXPECT_EQ("ABC", result.value());
}
// Check text normalization for *key*
{
// "ゔ"
const char kToBeReplaced[] = "\xE3\x82\x94";
// "ヴ"
const char kReplaced[] = "\xE3\x83\xB4";
// SessionOutput::FillConversionResult assumes that
// the given value is already normalized.
commands::Result result;
SessionOutput::FillConversionResult(
kToBeReplaced, kReplaced, &result);
EXPECT_EQ(commands::Result::STRING, result.type());
EXPECT_EQ(kReplaced, result.key());
EXPECT_EQ(kReplaced, result.value());
}
}
TEST(SessionOutputTest, FillPreeditResult) {
{
commands::Result result;
SessionOutput::FillPreeditResult("ABC", &result);
EXPECT_EQ(commands::Result::STRING, result.type());
EXPECT_EQ("ABC", result.key());
EXPECT_EQ("ABC", result.value());
}
{
// "ゔ"
const char kToBeReplaced[] = "\xE3\x82\x94";
// "ヴ"
const char kReplaced[] = "\xE3\x83\xB4";
commands::Result result;
SessionOutput::FillPreeditResult(kToBeReplaced, &result);
EXPECT_EQ(commands::Result::STRING, result.type());
EXPECT_EQ(kReplaced, result.key());
EXPECT_EQ(kReplaced, result.value());
}
}
TEST(SessionOutputTest, FillAllCandidateWords_NonForcused) {
// Test against b/3059255
// Even when no candidate was focused, all_candidate_words had focused_index.
CandidateList main_list(true);
commands::CandidateList candidates_proto;
main_list.AddCandidate(0, "key");
// Initialize Segment
Segment segment;
const char *kNormalKey = "key";
segment.set_key(kNormalKey);
Segment::Candidate *candidate = segment.push_back_candidate();
candidate->content_key = "key";
candidate->value = "value";
{
// Exexcute FillAllCandidateWords
const commands::Category kCategory = commands::SUGGESTION;
SessionOutput::FillAllCandidateWords(segment, main_list, kCategory,
&candidates_proto);
// Varidation
EXPECT_FALSE(candidates_proto.has_focused_index());
}
{
main_list.set_focused(true);
// Exexcute FillAllCandidateWords
// When the category is SUGGESTION, has_focused_index never return true in
// real usage. This is just a testcase.
const commands::Category kCategory = commands::SUGGESTION;
SessionOutput::FillAllCandidateWords(segment, main_list, kCategory,
&candidates_proto);
// Varidation
// If a candidate is forcused, true is expected.
EXPECT_TRUE(candidates_proto.has_focused_index());
}
}
} // namespace session
} // namespace mozc