/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.Mockito;
import com.vaadin.data.HasValue.ValueChangeEvent;
import com.vaadin.data.provider.DataProvider;
import com.vaadin.event.selection.MultiSelectionEvent;
import com.vaadin.event.selection.MultiSelectionListener;
import com.vaadin.server.ServerRpcManager;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.selection.MultiSelectServerRpc;
@RunWith(Parameterized.class)
public class AbstractMultiSelectTest<S extends AbstractMultiSelect<String>> {
@Parameters(name = "{0}")
public static Iterable<?> multiSelects() {
return Arrays.asList(new CheckBoxGroup<>(), new TwinColSelect<>(),
new ListSelect<>());
}
@Parameter
public S selectToTest;
private MultiSelectServerRpc rpc;
private Registration registration;
private List<Set<String>> values;
private List<Set<String>> oldValues;
@Before
public void setUp() {
selectToTest.deselectAll();
// Intentional deviation from upcoming selection order
selectToTest.setItems("3", "2", "1", "5", "8", "7", "4", "6");
rpc = ServerRpcManager.getRpcProxy(selectToTest,
MultiSelectServerRpc.class);
values = new ArrayList<>();
oldValues = new ArrayList<>();
selectToTest
.addValueChangeListener(event -> values.add(event.getValue()));
selectToTest.addValueChangeListener(
event -> oldValues.add(event.getOldValue()));
}
@After
public void tearDown() {
if (registration != null) {
registration.remove();
registration = null;
}
}
@Test
public void stableSelectionOrder() {
selectToTest.select("1");
selectToTest.select("2");
selectToTest.select("3");
assertSelectionOrder("1", "2", "3");
selectToTest.deselect("1");
assertSelectionOrder("2", "3");
selectToTest.select("1");
assertSelectionOrder("2", "3", "1");
selectToTest.select("7", "8", "4");
assertSelectionOrder("2", "3", "1", "7", "8", "4");
selectToTest.deselect("2", "1", "4", "5");
assertSelectionOrder("3", "7", "8");
selectToTest.updateSelection(
new LinkedHashSet<>(Arrays.asList("5", "2")),
new LinkedHashSet<>(Arrays.asList("3", "8")));
assertSelectionOrder("7", "5", "2");
verifyValueChangeEvents();
}
@Test
public void apiSelectionChange_notUserOriginated() {
AtomicInteger listenerCount = new AtomicInteger(0);
listenerCount.set(0);
registration = selectToTest.addSelectionListener(event -> {
listenerCount.incrementAndGet();
Assert.assertFalse(event.isUserOriginated());
});
selectToTest.select("1");
selectToTest.select("2");
selectToTest.deselect("2");
selectToTest.deselectAll();
selectToTest.select("2", "3", "4");
selectToTest.deselect("1", "4");
Assert.assertEquals(6, listenerCount.get());
// select partly selected
selectToTest.select("2", "3", "4");
Assert.assertEquals(7, listenerCount.get());
// select completely selected
selectToTest.select("2", "3", "4");
Assert.assertEquals(7, listenerCount.get());
// deselect partly not selected
selectToTest.select("1", "4");
Assert.assertEquals(8, listenerCount.get());
// deselect completely not selected
selectToTest.select("1", "4");
Assert.assertEquals(8, listenerCount.get());
verifyValueChangeEvents();
}
@Test
public void rpcSelectionChange_userOriginated() {
AtomicInteger listenerCount = new AtomicInteger(0);
registration = selectToTest.addSelectionListener(event -> {
listenerCount.incrementAndGet();
Assert.assertTrue(event.isUserOriginated());
});
rpcSelect("1");
assertSelectionOrder("1");
rpcSelect("2");
assertSelectionOrder("1", "2");
rpcDeselectItems("2");
assertSelectionOrder("1");
rpcSelect("3", "6");
assertSelectionOrder("1", "3", "6");
rpcDeselectItems("1", "3");
assertSelectionOrder("6");
Assert.assertEquals(5, listenerCount.get());
// select partly selected
rpcSelect("2", "3", "4");
Assert.assertEquals(6, listenerCount.get());
assertSelectionOrder("6", "2", "3", "4");
// select completely selected
rpcSelect("2", "3", "4");
Assert.assertEquals(6, listenerCount.get());
assertSelectionOrder("6", "2", "3", "4");
// deselect partly not selected
rpcDeselectItems("1", "4");
Assert.assertEquals(7, listenerCount.get());
assertSelectionOrder("6", "2", "3");
// deselect completely not selected
rpcDeselectItems("1", "4");
Assert.assertEquals(7, listenerCount.get());
assertSelectionOrder("6", "2", "3");
// select completely selected and deselect completely not selected
rpcUpdateSelection(new String[] { "3" }, new String[] { "1", "4" });
Assert.assertEquals(7, listenerCount.get());
assertSelectionOrder("6", "2", "3");
// select partly selected and deselect completely not selected
rpcUpdateSelection(new String[] { "4", "2" },
new String[] { "1", "8" });
Assert.assertEquals(8, listenerCount.get());
assertSelectionOrder("6", "2", "3", "4");
// select completely selected and deselect partly not selected
rpcUpdateSelection(new String[] { "4", "3" },
new String[] { "1", "2" });
Assert.assertEquals(9, listenerCount.get());
assertSelectionOrder("6", "3", "4");
// duplicate case - ignored
rpcUpdateSelection(new String[] { "2" }, new String[] { "2" });
Assert.assertEquals(9, listenerCount.get());
assertSelectionOrder("6", "3", "4");
// duplicate case - duplicate removed
rpcUpdateSelection(new String[] { "2" }, new String[] { "2", "3" });
Assert.assertEquals(10, listenerCount.get());
assertSelectionOrder("6", "4");
// duplicate case - duplicate removed
rpcUpdateSelection(new String[] { "6", "8" }, new String[] { "6" });
Assert.assertEquals(11, listenerCount.get());
assertSelectionOrder("6", "4", "8");
verifyValueChangeEvents();
}
@Test
public void getValue() {
selectToTest.select("1");
Assert.assertEquals(Collections.singleton("1"),
selectToTest.getValue());
selectToTest.deselectAll();
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("1");
set.add("5");
selectToTest.select(set.toArray(new String[2]));
Assert.assertEquals(set, selectToTest.getValue());
set.add("3");
selectToTest.select("3");
Assert.assertEquals(set, selectToTest.getValue());
verifyValueChangeEvents();
}
@Test
@SuppressWarnings({ "serial", "unchecked" })
public void getValue_isDelegatedTo_getSelectedItems() {
Set<String> set = Mockito.mock(Set.class);
AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() {
@Override
public Set<String> getSelectedItems() {
return set;
}
@Override
public void setItems(Collection<String> items) {
throw new UnsupportedOperationException(
"Not implemented for this test");
}
@Override
public DataProvider<String, ?> getDataProvider() {
return null;
}
};
Assert.assertSame(set, select.getValue());
verifyValueChangeEvents();
}
@Test
public void setValue() {
selectToTest.setValue(Collections.singleton("1"));
Assert.assertEquals(Collections.singleton("1"),
selectToTest.getSelectedItems());
Set<String> set = new LinkedHashSet<>();
set.add("4");
set.add("3");
selectToTest.setValue(set);
Assert.assertEquals(set, selectToTest.getSelectedItems());
verifyValueChangeEvents();
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
public void setValue_isDelegatedToDeselectAndUpdateSelection() {
AbstractMultiSelect<String> select = Mockito
.mock(AbstractMultiSelect.class);
Set set = new LinkedHashSet<>();
set.add("foo1");
set.add("foo");
Set selected = new LinkedHashSet<>();
selected.add("bar1");
selected.add("bar");
selected.add("bar2");
Mockito.when(select.getSelectedItems()).thenReturn(selected);
Mockito.doCallRealMethod().when(select).setValue(Mockito.anySet());
select.setValue(set);
Mockito.verify(select).updateSelection(set, selected);
}
@SuppressWarnings({ "unchecked", "serial" })
@Test
public void addValueChangeListener() {
AtomicReference<MultiSelectionListener<String>> selectionListener = new AtomicReference<>();
Registration registration = Mockito.mock(Registration.class);
Set<String> set = new HashSet<>();
set.add("foo");
set.add("bar");
AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() {
@Override
public Registration addSelectionListener(
MultiSelectionListener<String> listener) {
selectionListener.set(listener);
return registration;
}
@Override
public Set<String> getValue() {
return set;
}
@Override
public void setItems(Collection<String> items) {
throw new UnsupportedOperationException(
"Not implemented for this test");
}
@Override
public DataProvider<String, ?> getDataProvider() {
return null;
}
};
AtomicReference<ValueChangeEvent<?>> event = new AtomicReference<>();
Registration actualRegistration = select.addValueChangeListener(evt -> {
Assert.assertNull(event.get());
event.set(evt);
});
Assert.assertSame(registration, actualRegistration);
selectionListener.get().selectionChange(new MultiSelectionEvent<>(
select, Mockito.mock(Set.class), true));
Assert.assertEquals(select, event.get().getComponent());
Assert.assertEquals(set, event.get().getValue());
Assert.assertTrue(event.get().isUserOriginated());
}
private void rpcSelect(String... keysToSelect) {
rpcUpdateSelection(keysToSelect, new String[] {});
}
private void rpcDeselectItems(String... keysToDeselect) {
rpcUpdateSelection(new String[] {}, keysToDeselect);
}
private void rpcUpdateSelection(String[] added, String[] removed) {
rpc.updateSelection(
new LinkedHashSet<>(Stream.of(added).map(this::getItemKey)
.collect(Collectors.toList())),
new LinkedHashSet<>(Stream.of(removed).map(this::getItemKey)
.collect(Collectors.toList())));
}
private String getItemKey(String dataObject) {
return selectToTest.getDataCommunicator().getKeyMapper()
.key(dataObject);
}
private void assertSelectionOrder(String... selectionOrder) {
Assert.assertEquals(Arrays.asList(selectionOrder),
new ArrayList<>(selectToTest.getSelectedItems()));
}
private void verifyValueChangeEvents() {
if (oldValues.size() > 0) {
Assert.assertTrue(oldValues.get(0).isEmpty());
Assert.assertEquals(values.size(), oldValues.size());
for (int i = 0; i < oldValues.size() - 1; i++) {
Assert.assertEquals(values.get(i), oldValues.get(i + 1));
}
}
}
}