blob: 0a8fd83eb91dbcf768bf78c1bb86b9d8ffa9d219 [file] [log] [blame]
// Copyright 2010-2014, 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.
package org.mozc.android.inputmethod.japanese.ui;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.geq;
import static org.easymock.EasyMock.getCurrentArguments;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.leq;
import static org.easymock.EasyMock.same;
import org.mozc.android.inputmethod.japanese.protobuf.ProtoCandidates.Annotation;
import org.mozc.android.inputmethod.japanese.protobuf.ProtoCandidates.CandidateList;
import org.mozc.android.inputmethod.japanese.protobuf.ProtoCandidates.CandidateWord;
import org.mozc.android.inputmethod.japanese.testing.InstrumentationTestCaseWithMock;
import org.mozc.android.inputmethod.japanese.ui.CandidateLayout.Row;
import org.mozc.android.inputmethod.japanese.ui.CandidateLayout.Span;
import org.mozc.android.inputmethod.japanese.ui.CandidateLayoutRenderer.DescriptionLayoutPolicy;
import org.mozc.android.inputmethod.japanese.ui.CandidateLayoutRenderer.ValueScalingPolicy;
import org.mozc.android.inputmethod.japanese.view.CarrierEmojiRenderHelper;
import com.google.common.base.Optional;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import org.easymock.IAnswer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*/
public class CandidateLayoutRendererTest extends InstrumentationTestCaseWithMock {
public void testDrawCandidateLayout() {
// Create 5x5 dummy candidate list.
CandidateList.Builder builder = CandidateList.newBuilder();
for (int i = 0; i < 25; ++i) {
builder.addCandidates(CandidateWord.newBuilder()
.setId(i)
.setIndex(i));
}
// Set focused index to 7-th (2-row, 2-column) candidate word.
builder.setFocusedIndex(6);
CandidateList candidateList = builder.build();
// Then layout it to 10px x 10px grid.
List<Row> rowList = new ArrayList<Row>(5);
{
int index = 0;
for (int i = 0; i < 5; ++i) {
Row row = new Row();
rowList.add(row);
row.setTop(10 * i);
row.setHeight(10);
row.setWidth(50);
for (int j = 0; j < 5; ++j) {
// Insert a null candidate word span
Optional<CandidateWord> candidateWord = (i == 2 && j == 0)
? Optional.<CandidateWord>absent()
: Optional.of(candidateList.getCandidates(index++));
Span span = new Span(candidateWord, 0, 0, Collections.<String>emptyList());
span.setLeft(j * 10);
span.setRight((j + 1) * 10);
row.addSpan(span);
}
}
}
CandidateLayout candidateLayout = new CandidateLayout(rowList, 50, 50);
CandidateLayoutRenderer renderer = createMockBuilder(CandidateLayoutRenderer.class)
.addMockedMethod("drawSpan")
.createMock();
// Test if the focused index is updated by setCandidateList.
resetAll();
replayAll();
renderer.setCandidateList(Optional.of(candidateList));
verifyAll();
assertEquals(6, renderer.focusedIndex);
// Test if the focused index is -1 for absent candidate list.
resetAll();
replayAll();
renderer.setCandidateList(Optional.<CandidateList>absent());
verifyAll();
assertEquals(-1, renderer.focusedIndex);
// drawCandidateLayout is tested below. Set up CarrrierEmojiRenderHelper and Canvas mocks.
View targetView = createViewMock(View.class);
CarrierEmojiRenderHelper carrierEmojiRenderHelper =
createMockBuilder(CarrierEmojiRenderHelper.class)
.withConstructor(View.class)
.withArgs(targetView)
.createNiceMock();
Canvas canvas = createMockBuilder(Canvas.class)
.addMockedMethod("getClipBounds", Rect.class)
.addMockedMethod("drawLine")
.createMock();
// Set the candidate list to both renderer and carrierEmojiRenderHelper.
resetAll();
replayAll();
renderer.setCandidateList(Optional.of(candidateList));
carrierEmojiRenderHelper.setCandidateList(Optional.of(candidateList));
verifyAll();
assertEquals(6, renderer.focusedIndex);
// Then render the layout to the canvas.
resetAll();
// Dummy clip of the canvas. It's (15, 15) - (35, 35).
expect(canvas.getClipBounds(isA(Rect.class))).andStubAnswer(new IAnswer<Boolean>() {
@Override
public Boolean answer() throws Throwable {
Rect.class.cast(getCurrentArguments()[0]).set(15, 15, 35, 35);
return Boolean.TRUE;
}
});
// All spans which are in the bounding box (or crossing the boundary) should be rendered.
// (1, 1) element is focused (specified by CandidateList#getFocusedIndex), and (1, 3) element is
// pressed (set "8" in the drawCandidateLayout's argument below). In this case, separators are
// drawn on the right edge of each span because each span to be rendered is not rightmost.
// Separator length is not tested but should be less than or equal to the edge length, i.e., 10.
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(1)), eq(true),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(10f), eq(10f), leq(20f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(10f), eq(20f), leq(20f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(3)), eq(true),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(10f), eq(30f), leq(20f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(1)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(20f), eq(10f), leq(30f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(20f), eq(20f), leq(30f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(3)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(20f), eq(30f), leq(30f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(1)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(30f), eq(10f), leq(40f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(30f), eq(20f), leq(40f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(3)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(30f), eq(30f), leq(40f), isA(Paint.class));
replayAll();
// 8 is the pseudo pressing candidate.
renderer.drawCandidateLayout(canvas, candidateLayout, 8, carrierEmojiRenderHelper);
verifyAll();
// Next, test rendering on the whole canvas.
resetAll();
expect(canvas.getClipBounds(isA(Rect.class))).andStubAnswer(new IAnswer<Boolean>() {
@Override
public Boolean answer() throws Throwable {
Rect.class.cast(getCurrentArguments()[0]).set(0, 0, 50, 50);
return Boolean.TRUE;
}
});
// Note that, in each row, a separator is not drawn after the last span, i.e., drawn only
// between spans. The focused and pressed spans are the same as before.
// Row 0
renderer.drawSpan(
same(canvas), same(rowList.get(0)), same(rowList.get(0).getSpanList().get(0)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(0f), eq(10f), leq(10f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(0)), same(rowList.get(0).getSpanList().get(1)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(0f), eq(20f), leq(10f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(0)), same(rowList.get(0).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(0f), eq(30f), leq(10f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(0)), same(rowList.get(0).getSpanList().get(3)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(40f), geq(0f), eq(40f), leq(10f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(0)), same(rowList.get(0).getSpanList().get(4)), eq(false),
same(carrierEmojiRenderHelper));
// Row 1
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(0)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(10f), eq(10f), leq(20f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(1)), eq(true),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(10f), eq(20f), leq(20f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(10f), eq(30f), leq(20f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(3)), eq(true),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(40f), geq(10f), eq(40f), leq(20f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(1)), same(rowList.get(1).getSpanList().get(4)), eq(false),
same(carrierEmojiRenderHelper));
// Row 2
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(0)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(20f), eq(10f), leq(30f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(1)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(20f), eq(20f), leq(30f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(20f), eq(30f), leq(30f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(3)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(40f), geq(20f), eq(40f), leq(30f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(2)), same(rowList.get(2).getSpanList().get(4)), eq(false),
same(carrierEmojiRenderHelper));
// Row 3
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(0)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(30f), eq(10f), leq(40f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(1)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(30f), eq(20f), leq(40f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(30f), eq(30f), leq(40f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(3)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(40f), geq(30f), eq(40f), leq(40f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(3)), same(rowList.get(3).getSpanList().get(4)), eq(false),
same(carrierEmojiRenderHelper));
// Row 4
renderer.drawSpan(
same(canvas), same(rowList.get(4)), same(rowList.get(4).getSpanList().get(0)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(10f), geq(40f), eq(10f), leq(50f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(4)), same(rowList.get(4).getSpanList().get(1)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(20f), geq(40f), eq(20f), leq(50f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(4)), same(rowList.get(4).getSpanList().get(2)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(30f), geq(40f), eq(30f), leq(50f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(4)), same(rowList.get(4).getSpanList().get(3)), eq(false),
same(carrierEmojiRenderHelper));
canvas.drawLine(eq(40f), geq(40f), eq(40f), leq(50f), isA(Paint.class));
renderer.drawSpan(
same(canvas), same(rowList.get(4)), same(rowList.get(4).getSpanList().get(4)), eq(false),
same(carrierEmojiRenderHelper));
replayAll();
renderer.drawCandidateLayout(canvas, candidateLayout, 8, carrierEmojiRenderHelper);
verifyAll();
}
public void testVeryLongValueAndDescription() {
String veryLongWord = "";
for (int i = 0; i < 64; ++i) {
veryLongWord += "ああああ";
}
CandidateList candidateList = CandidateList.newBuilder()
.addCandidates(CandidateWord.newBuilder()
.setId(0)
.setIndex(0)
.setKey(veryLongWord)
.setValue(veryLongWord)
.setAnnotation(Annotation.newBuilder()
.setDescription(veryLongWord)))
.build();
CandidateLayoutRenderer renderer = new CandidateLayoutRenderer();
renderer.setCandidateList(Optional.of(candidateList));
renderer.setDescriptionLayoutPolicy(DescriptionLayoutPolicy.EXCLUSIVE);
renderer.setValueScalingPolicy(ValueScalingPolicy.HORIZONTAL);
int textSize = 10;
renderer.setValueTextSize(textSize);
renderer.setDescriptionTextSize(textSize);
Paint paint = new Paint();
paint.setTextSize(textSize);
float wordWidth = paint.measureText(veryLongWord);
Row row = new Row();
row.setTop(10);
row.setHeight(10);
row.setWidth(10);
Span span = new Span(
Optional.of(candidateList.getCandidates(0)), wordWidth, wordWidth,
Collections.<String>emptyList());
span.setLeft(0);
span.setRight(10);
row.addSpan(span);
List<Row> rowList = Collections.singletonList(row);
CandidateLayout candidateLayout = new CandidateLayout(rowList, 10, 10);
View targetView = createViewMock(View.class);
CarrierEmojiRenderHelper carrierEmojiRenderHelper =
createMockBuilder(CarrierEmojiRenderHelper.class)
.withConstructor(View.class)
.withArgs(targetView)
.createNiceMock();
Canvas canvas = createMockBuilder(Canvas.class)
.addMockedMethod("getClipBounds", Rect.class)
.createNiceMock();
expect(canvas.getClipBounds(isA(Rect.class))).andStubAnswer(new IAnswer<Boolean>() {
@Override
public Boolean answer() throws Throwable {
Rect.class.cast(getCurrentArguments()[0]).set(0, 0, 10, 10);
return Boolean.TRUE;
}
});
replayAll();
renderer.drawCandidateLayout(canvas, candidateLayout, 0, carrierEmojiRenderHelper);
verifyAll();
}
}