chromium/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.omnibox.suggestions;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import static org.chromium.components.omnibox.GroupConfigTestSupport.SECTION_1_NO_HEADER;
import static org.chromium.components.omnibox.GroupConfigTestSupport.SECTION_2_WITH_HEADER;
import static org.chromium.components.omnibox.GroupConfigTestSupport.SECTION_3_WITH_HEADER;
import static org.chromium.components.omnibox.GroupConfigTestSupport.SECTION_MOST_VISITED;

import android.content.Context;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.omnibox.suggestions.groupseparator.GroupSeparatorProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderProcessor;
import org.chromium.components.omnibox.AutocompleteMatch;
import org.chromium.components.omnibox.AutocompleteMatchBuilder;
import org.chromium.components.omnibox.AutocompleteResult;
import org.chromium.components.omnibox.GroupsProto.GroupConfig;
import org.chromium.components.omnibox.GroupsProto.GroupsInfo;
import org.chromium.components.omnibox.OmniboxSuggestionType;
import org.chromium.components.omnibox.suggestions.OmniboxSuggestionUiType;
import org.chromium.ui.modelutil.PropertyModel;

import java.util.ArrayList;
import java.util.List;

/** Tests for {@link DropdownItemViewInfoListBuilder}. */
@RunWith(BaseRobolectricTestRunner.class)
public class DropdownItemViewInfoListBuilderUnitTest {
    public @Rule MockitoRule mockitoRule = MockitoJUnit.rule();

    private Context mContext = ContextUtils.getApplicationContext();

    private @Mock AutocompleteController mAutocompleteController;
    private @Mock SuggestionProcessor mMockSuggestionProcessor;
    private @Spy HeaderProcessor mMockHeaderProcessor = new HeaderProcessor(mContext);

    private GroupSeparatorProcessor mGroupSeparatorProcessor =
            new GroupSeparatorProcessor(mContext);
    DropdownItemViewInfoListBuilder mBuilder;

    @Before
    public void setUp() {
        when(mMockSuggestionProcessor.createModel())
                .thenAnswer((mock) -> new PropertyModel(SuggestionCommonProperties.ALL_KEYS));
        when(mMockSuggestionProcessor.getViewTypeId()).thenReturn(OmniboxSuggestionUiType.DEFAULT);

        mBuilder = new DropdownItemViewInfoListBuilder(() -> null, (url) -> false);
        mBuilder.registerSuggestionProcessor(mMockSuggestionProcessor);
        mBuilder.setGroupSeparatorProcessorForTest(mGroupSeparatorProcessor);
        mBuilder.setHeaderProcessorForTest(mMockHeaderProcessor);
    }

    /**
     * Verify corner rounding and separator presence on a specific model.
     *
     * @param model the model to verify
     * @param wantTopCornersRounded expected rounding state of top corners
     * @param wantBottomCornersRounded expected rounding state of bottom corners
     * @param wantSeparator expected state of the separator
     */
    void verifyRounding(
            PropertyModel model,
            boolean wantTopCornersRounded,
            boolean wantBottomCornersRounded,
            boolean wantSeparator) {
        Assert.assertEquals(
                wantTopCornersRounded, model.get(DropdownCommonProperties.BG_TOP_CORNER_ROUNDED));
        Assert.assertEquals(
                wantBottomCornersRounded,
                model.get(DropdownCommonProperties.BG_BOTTOM_CORNER_ROUNDED));
        Assert.assertEquals(wantSeparator, model.get(DropdownCommonProperties.SHOW_DIVIDER));
    }

    @Test
    public void buildDropdownViewInfoList_mixedGroups() {
        final var groupsDetails =
                GroupsInfo.newBuilder()
                        .putGroupConfigs(1, SECTION_MOST_VISITED)
                        .putGroupConfigs(2, SECTION_2_WITH_HEADER)
                        .build();

        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);

        AutocompleteMatch horizontal =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        AutocompleteMatch vertical =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(2)
                        .build();

        var actualList =
                List.of(horizontal, horizontal, vertical, vertical, horizontal, horizontal);
        var model =
                mBuilder.buildDropdownViewInfoList(
                        AutocompleteResult.fromCache(actualList, groupsDetails));

        // 1 horizontal + 1 header + 2 vertical + 1 horizontal.
        Assert.assertEquals(5, model.size());

        // Check reported positions in list.
        verify(mMockSuggestionProcessor, atLeastOnce()).doesProcessSuggestion(horizontal, 0);
        verify(mMockSuggestionProcessor).doesProcessSuggestion(vertical, 1);
        verify(mMockSuggestionProcessor).doesProcessSuggestion(vertical, 2);
        verify(mMockSuggestionProcessor, atLeastOnce()).doesProcessSuggestion(horizontal, 3);

        verify(mMockSuggestionProcessor, times(2)).populateModel(eq(horizontal), any(), eq(0));
        verify(mMockSuggestionProcessor).populateModel(eq(vertical), any(), eq(1));
        verify(mMockSuggestionProcessor).populateModel(eq(vertical), any(), eq(2));
        verify(mMockSuggestionProcessor, times(2)).populateModel(eq(horizontal), any(), eq(3));

        // Other calls we expect to see.
        verify(mMockSuggestionProcessor).onSuggestionsReceived();
        verify(mMockSuggestionProcessor, times(4)).createModel();
        verify(mMockSuggestionProcessor, atLeastOnce()).getViewTypeId();
        verifyNoMoreInteractions(mMockSuggestionProcessor);
    }

    @Test
    public void headers_buildsHeadersOnlyWhenGroupChanges() {
        final List<AutocompleteMatch> actualList = new ArrayList<>();
        final var groupsDetails =
                GroupsInfo.newBuilder()
                        .putGroupConfigs(1, SECTION_2_WITH_HEADER)
                        .putGroupConfigs(2, SECTION_3_WITH_HEADER)
                        .build();

        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);
        AutocompleteMatch suggestionWithNoGroup =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .build();
        AutocompleteMatch suggestionForGroup1 =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        AutocompleteMatch suggestionForGroup2 =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(2)
                        .build();

        actualList.add(suggestionWithNoGroup);
        actualList.add(suggestionForGroup1);
        actualList.add(suggestionForGroup1);
        actualList.add(suggestionForGroup2);
        actualList.add(suggestionForGroup2);

        final InOrder verifier = inOrder(mMockSuggestionProcessor, mMockHeaderProcessor);
        final List<DropdownItemViewInfo> model =
                mBuilder.buildDropdownViewInfoList(
                        AutocompleteResult.fromCache(actualList, groupsDetails));

        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionWithNoGroup), any(), eq(0));
        verifier.verify(mMockHeaderProcessor, times(1))
                .populateModel(any(), eq(SECTION_2_WITH_HEADER.getHeaderText()));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup1), any(), eq(1));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup1), any(), eq(2));
        verifier.verify(mMockHeaderProcessor, times(1))
                .populateModel(any(), eq(SECTION_3_WITH_HEADER.getHeaderText()));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup2), any(), eq(3));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup2), any(), eq(4));
        Assert.assertEquals(7, model.size()); // 2 headers + 5 suggestions.

        var defaultGroupConfig = GroupConfig.getDefaultInstance();

        Assert.assertEquals(model.get(0).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(0).groupConfig, defaultGroupConfig);
        verifyRounding(model.get(0).model, true, true, false);

        Assert.assertEquals(model.get(1).type, OmniboxSuggestionUiType.HEADER);
        Assert.assertEquals(model.get(1).groupConfig, SECTION_2_WITH_HEADER);
        verifyRounding(model.get(1).model, false, false, false);

        Assert.assertEquals(model.get(2).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(2).groupConfig, SECTION_2_WITH_HEADER);
        verifyRounding(model.get(2).model, true, false, true);

        Assert.assertEquals(model.get(3).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(3).groupConfig, SECTION_2_WITH_HEADER);
        verifyRounding(model.get(3).model, false, true, false);

        Assert.assertEquals(model.get(4).type, OmniboxSuggestionUiType.HEADER);
        Assert.assertEquals(model.get(4).groupConfig, SECTION_3_WITH_HEADER);
        verifyRounding(model.get(4).model, false, false, false);

        Assert.assertEquals(model.get(5).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(5).groupConfig, SECTION_3_WITH_HEADER);
        verifyRounding(model.get(5).model, true, false, true);
        Assert.assertEquals(model.get(6).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(6).groupConfig, SECTION_3_WITH_HEADER);
        verifyRounding(model.get(6).model, false, true, false);
    }

    @Test
    public void headers_respectGroupHeadersWithNoTitle() {
        final List<AutocompleteMatch> actualList = new ArrayList<>();
        final var groupsDetails =
                GroupsInfo.newBuilder()
                        .putGroupConfigs(1, SECTION_1_NO_HEADER)
                        .putGroupConfigs(2, SECTION_2_WITH_HEADER)
                        .build();

        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);
        AutocompleteMatch suggestionWithNoGroup =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .build();
        AutocompleteMatch suggestionForGroup1 =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        AutocompleteMatch suggestionForGroup2 =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(2)
                        .build();

        actualList.add(suggestionWithNoGroup);
        actualList.add(suggestionForGroup1);
        actualList.add(suggestionForGroup1);
        actualList.add(suggestionForGroup2);
        actualList.add(suggestionForGroup2);

        final InOrder verifier = inOrder(mMockSuggestionProcessor, mMockHeaderProcessor);
        final List<DropdownItemViewInfo> model =
                mBuilder.buildDropdownViewInfoList(
                        AutocompleteResult.fromCache(actualList, groupsDetails));

        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionWithNoGroup), any(), eq(0));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup1), any(), eq(1));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup1), any(), eq(2));
        verifier.verify(mMockHeaderProcessor, times(1))
                .populateModel(any(), eq(SECTION_2_WITH_HEADER.getHeaderText()));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup2), any(), eq(3));
        verifier.verify(mMockSuggestionProcessor, times(1))
                .populateModel(eq(suggestionForGroup2), any(), eq(4));

        // Make sure no other headers were ever constructed.
        verify(mMockHeaderProcessor, times(1)).populateModel(any(), any());

        var defaultGroupConfig = GroupConfig.getDefaultInstance();

        // We're showing:
        // - 1 suggestion,
        // - <separator>
        // - 2 suggestions (grouped),
        // - <header>
        // - 2 suggestions (grouped).
        Assert.assertEquals(7, model.size());

        Assert.assertEquals(model.get(0).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(0).groupConfig, defaultGroupConfig);

        Assert.assertEquals(model.get(1).type, OmniboxSuggestionUiType.GROUP_SEPARATOR);
        Assert.assertEquals(model.get(1).groupConfig, SECTION_1_NO_HEADER);

        Assert.assertEquals(model.get(2).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(2).groupConfig, SECTION_1_NO_HEADER);
        Assert.assertEquals(model.get(3).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(3).groupConfig, SECTION_1_NO_HEADER);

        Assert.assertEquals(model.get(4).type, OmniboxSuggestionUiType.HEADER);
        Assert.assertEquals(model.get(4).groupConfig, SECTION_2_WITH_HEADER);
        Assert.assertEquals(model.get(5).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(5).groupConfig, SECTION_2_WITH_HEADER);
        Assert.assertEquals(model.get(6).type, OmniboxSuggestionUiType.DEFAULT);
        Assert.assertEquals(model.get(6).groupConfig, SECTION_2_WITH_HEADER);
    }

    @Test
    public void builder_propagatesOmniboxSessionStateChangeEvents() {
        mBuilder.onOmniboxSessionStateChange(true);
        verify(mMockHeaderProcessor, times(1)).onOmniboxSessionStateChange(eq(true));
        verify(mMockSuggestionProcessor, times(1)).onOmniboxSessionStateChange(eq(true));

        mBuilder.onOmniboxSessionStateChange(false);
        verify(mMockHeaderProcessor, times(1)).onOmniboxSessionStateChange(eq(false));
        verify(mMockSuggestionProcessor, times(1)).onOmniboxSessionStateChange(eq(false));

        verifyNoMoreInteractions(mMockHeaderProcessor);
        verifyNoMoreInteractions(mMockSuggestionProcessor);
    }

    @Test
    public void builder_propagatesNativeInitializedEvent() {
        mBuilder.onNativeInitialized();
        verify(mMockHeaderProcessor, times(1)).onNativeInitialized();
        verify(mMockSuggestionProcessor, times(1)).onNativeInitialized();

        verifyNoMoreInteractions(mMockHeaderProcessor);
        verifyNoMoreInteractions(mMockSuggestionProcessor);
    }

    @Test
    public void buildVerticalSuggestionsGroup_withoutGroupHeader_noPreviousGroup() {
        var match =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        var matches = List.of(match, match);
        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);
        clearInvocations(mMockHeaderProcessor, mMockSuggestionProcessor);

        var result =
                mBuilder.buildVerticalSuggestionsGroup(
                        SECTION_1_NO_HEADER, null, matches, /* firstVerticalPosition= */ 5);

        verify(mMockSuggestionProcessor, times(2)).createModel();
        verify(mMockSuggestionProcessor, atLeastOnce()).getViewTypeId();
        verify(mMockSuggestionProcessor).doesProcessSuggestion(match, 5);
        verify(mMockSuggestionProcessor).populateModel(eq(match), any(), eq(5));
        verify(mMockSuggestionProcessor).doesProcessSuggestion(match, 6);
        verify(mMockSuggestionProcessor).populateModel(eq(match), any(), eq(6));

        verifyNoMoreInteractions(mMockHeaderProcessor, mMockSuggestionProcessor);

        assertEquals(/* 0 header + 2 suggestions = */ 2, result.size());
    }

    @Test
    public void buildVerticalSuggestionsGroup_withoutGroupHeader_verticalPreviousGroup() {
        var match =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        var matches = List.of(match, match);
        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);
        clearInvocations(mMockHeaderProcessor, mMockSuggestionProcessor);

        var result =
                mBuilder.buildVerticalSuggestionsGroup(
                        SECTION_1_NO_HEADER,
                        SECTION_2_WITH_HEADER,
                        matches,
                        /* firstVerticalPosition= */ 5);

        verify(mMockSuggestionProcessor, times(2)).createModel();
        verify(mMockSuggestionProcessor, atLeastOnce()).getViewTypeId();
        verify(mMockSuggestionProcessor).doesProcessSuggestion(match, 5);
        verify(mMockSuggestionProcessor).populateModel(eq(match), any(), eq(5));
        verify(mMockSuggestionProcessor).doesProcessSuggestion(match, 6);
        verify(mMockSuggestionProcessor).populateModel(eq(match), any(), eq(6));

        verifyNoMoreInteractions(mMockHeaderProcessor, mMockSuggestionProcessor);

        assertEquals(/* 1 space + 2 suggestions = */ 3, result.size());
        assertEquals(OmniboxSuggestionUiType.GROUP_SEPARATOR, result.get(0).type);
        assertEquals(OmniboxSuggestionUiType.DEFAULT, result.get(1).type);
        assertEquals(OmniboxSuggestionUiType.DEFAULT, result.get(2).type);
    }

    @Test
    public void buildVerticalSuggestionsGroup_withGroupHeader_noPreviousGroup() {
        var match =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        var matches = List.of(match, match);
        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);
        clearInvocations(mMockHeaderProcessor, mMockSuggestionProcessor);

        var result =
                mBuilder.buildVerticalSuggestionsGroup(
                        SECTION_2_WITH_HEADER, null, matches, /* firstVerticalPosition= */ 7);

        verify(mMockHeaderProcessor).createModel();
        verify(mMockHeaderProcessor, atLeastOnce()).getViewTypeId();
        verify(mMockHeaderProcessor)
                .populateModel(any(), eq(SECTION_2_WITH_HEADER.getHeaderText()));

        verify(mMockSuggestionProcessor, times(2)).createModel();
        verify(mMockSuggestionProcessor, atLeastOnce()).getViewTypeId();
        verify(mMockSuggestionProcessor).doesProcessSuggestion(match, 7);
        verify(mMockSuggestionProcessor).populateModel(eq(match), any(), eq(7));
        verify(mMockSuggestionProcessor).doesProcessSuggestion(match, 8);
        verify(mMockSuggestionProcessor).populateModel(eq(match), any(), eq(8));

        verifyNoMoreInteractions(mMockHeaderProcessor, mMockSuggestionProcessor);

        assertEquals(/* 1 header + 2 suggestions = */ 3, result.size());
        assertEquals(OmniboxSuggestionUiType.HEADER, result.get(0).type);
        assertEquals(OmniboxSuggestionUiType.DEFAULT, result.get(1).type);
        assertEquals(OmniboxSuggestionUiType.DEFAULT, result.get(2).type);
    }

    @Test
    public void buildHorizontalSuggestionsGroup_withoutGroupHeader() {
        var match =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        var matches = List.of(match, match);
        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);
        clearInvocations(mMockHeaderProcessor, mMockSuggestionProcessor);

        var result =
                mBuilder.buildHorizontalSuggestionsGroup(
                        SECTION_1_NO_HEADER, matches, /* firstVerticalPosition= */ 5);

        var captor = ArgumentCaptor.forClass(PropertyModel.class);
        verify(mMockSuggestionProcessor).getViewTypeId();
        verify(mMockSuggestionProcessor).createModel();
        verify(mMockSuggestionProcessor, atLeastOnce()).doesProcessSuggestion(match, 5);
        verify(mMockSuggestionProcessor, times(2))
                .populateModel(eq(match), captor.capture(), eq(5));
        verifyNoMoreInteractions(mMockHeaderProcessor, mMockSuggestionProcessor);

        assertEquals(/* 0 header + 1 suggestion row = */ 1, result.size());

        // Verify that the same PropertyModel was used to build UI element, and it's the one that
        // was returned.
        assertEquals(captor.getAllValues().get(0), captor.getAllValues().get(1));
        assertEquals(captor.getValue(), result.get(0).model);
    }

    @Test
    public void buildHorizontalSuggestionsGroup_withGroupHeader() {
        var match =
                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                        .setGroupId(1)
                        .build();
        var matches = List.of(match, match);
        when(mMockSuggestionProcessor.doesProcessSuggestion(any(), anyInt())).thenReturn(true);
        clearInvocations(mMockHeaderProcessor, mMockSuggestionProcessor);

        var result =
                mBuilder.buildHorizontalSuggestionsGroup(
                        SECTION_2_WITH_HEADER, matches, /* firstVerticalPosition= */ 7);

        verify(mMockHeaderProcessor).createModel();
        verify(mMockHeaderProcessor, atLeastOnce()).getViewTypeId();
        verify(mMockHeaderProcessor)
                .populateModel(any(), eq(SECTION_2_WITH_HEADER.getHeaderText()));

        var captor = ArgumentCaptor.forClass(PropertyModel.class);
        verify(mMockSuggestionProcessor).getViewTypeId();
        verify(mMockSuggestionProcessor).createModel();
        verify(mMockSuggestionProcessor, atLeastOnce()).doesProcessSuggestion(match, 7);
        verify(mMockSuggestionProcessor, times(2))
                .populateModel(eq(match), captor.capture(), eq(7));

        verifyNoMoreInteractions(mMockHeaderProcessor, mMockSuggestionProcessor);

        assertEquals(/* 1 header + 1 suggestion row = */ 2, result.size());

        // Verify that the same PropertyModel was used to build UI element, and it's the one that
        // was returned.
        assertEquals(captor.getAllValues().get(0), captor.getAllValues().get(1));
        assertEquals(captor.getValue(), result.get(1).model);
    }
}