/**
* Copyright 2011 Google Inc.
*
* 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 org.waveprotocol.wave.client.wave;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import junit.framework.TestCase;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.waveprotocol.wave.client.scheduler.testing.FakeTimerService;
import org.waveprotocol.wave.model.conversation.ConversationBlip;
import org.waveprotocol.wave.model.supplement.ObservableSupplementedWave;
import org.waveprotocol.wave.model.wave.Blip;
import org.waveprotocol.wave.model.wave.ObservableWavelet;
import org.waveprotocol.wave.model.wave.WaveletListener;
import org.waveprotocol.wave.model.wave.opbased.ObservableWaveView;
import java.util.Collections;
/**
* Tests for {@link LocalSupplementedWaveImpl}.
*
* @author hearnden@google.com (David Hearnden)
*/
public final class LocalSupplementedWaveImplTest extends TestCase {
@Mock
private ObservableSupplementedWave supplement;
@Mock
private ObservableWaveView wave;
@Mock
private ObservableWavelet root;
private ConversationBlip blip;
private LocalSupplementedWaveImpl target;
private FakeTimerService timer;
@Override
protected void setUp() {
MockitoAnnotations.initMocks(this);
blip = mockBlip();
timer = new FakeTimerService();
doReturn(Collections.singleton(root)).when(wave).getWavelets();
target = new LocalSupplementedWaveImpl(timer, wave, supplement);
target.init();
}
private ConversationBlip mockBlip() {
ConversationBlip blip = mock(ConversationBlip.class);
Blip raw = mock(Blip.class);
when(blip.hackGetRaw()).thenReturn(raw);
return blip;
}
public void testReadingActionsAreScoped() {
target.startReading(blip);
try {
target.startReading(blip);
fail();
} catch (IllegalStateException expected) {
}
target.stopReading(blip);
try {
target.stopReading(blip);
fail();
} catch (IllegalStateException expected) {
}
}
public void testReadingBlipIsAlwaysReadOtherwiseIsWhatSupplementSays() {
when(supplement.isUnread(blip)).thenReturn(true);
target.startReading(blip);
assertFalse(target.isUnread(blip));
// Stop reading, fast-forwarding to when auto-reading has ceased.
target.stopReading(blip);
timer.tick(LocalSupplementedWaveImpl.EVICT_TIME_MS);
assertTrue(target.isUnread(blip));
}
public void testNonreadingBlipIsWhatSupplementSays() {
when(supplement.isUnread(blip)).thenReturn(false);
assertFalse(target.isUnread(blip));
when(supplement.isUnread(blip)).thenReturn(true);
assertTrue(target.isUnread(blip));
}
public void testStopReadingCausesAutoReadingUntilEvicted() {
target.startReading(blip);
target.stopReading(blip);
reset(supplement); // Ignore anything that happened before now.
int invocations = LocalSupplementedWaveImpl.EVICT_TIME_MS / LocalSupplementedWaveImpl.REPEAT_MS;
for (int i = 0; i < invocations; i++) {
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
}
verify(supplement, times(invocations)).markAsRead(blip);
reset(supplement);
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
verify(supplement, never()).markAsRead(blip);
}
public void testMarkAsReadCausesAutoReadingUntilEvicted() {
target.markAsRead(blip);
reset(supplement); // Ignore anything that happened before now.
int invocations = LocalSupplementedWaveImpl.EVICT_TIME_MS / LocalSupplementedWaveImpl.REPEAT_MS;
for (int i = 0; i < invocations; i++) {
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
}
verify(supplement, times(invocations)).markAsRead(blip);
reset(supplement);
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
verify(supplement, never()).markAsRead(blip);
}
@SuppressWarnings("deprecation")
public void testRemoteOpStopsAutoReading() {
ArgumentCaptor<WaveletListener> arg = ArgumentCaptor.forClass(WaveletListener.class);
verify(root).addListener(arg.capture());
WaveletListener listener = arg.getValue();
target.markAsRead(blip);
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
reset(supplement); // Ignore anything that happened before now.
listener.onRemoteBlipContentModified(root, blip.hackGetRaw());
// Expect that target removed the blip from the auto-read collection.
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
verify(supplement, never()).markAsRead(blip);
}
public void testAutoReadSupportsManyBlips() {
ConversationBlip blip2 = mockBlip();
target.markAsRead(blip);
target.markAsRead(blip2);
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
verify(supplement, times(2)).markAsRead(blip);
verify(supplement, times(2)).markAsRead(blip2);
}
public void testMarkAsReadRefreshesEvictionTime() {
target.markAsRead(blip);
// Fast-forward to near eviction time.
timer.tick(LocalSupplementedWaveImpl.EVICT_TIME_MS - 1);
// Refresh, and expect a full set of future markings.
target.markAsRead(blip);
reset(supplement);
int invocations = LocalSupplementedWaveImpl.EVICT_TIME_MS / LocalSupplementedWaveImpl.REPEAT_MS;
for (int i = 0; i < invocations; i++) {
timer.tick(LocalSupplementedWaveImpl.REPEAT_MS);
}
verify(supplement, times(invocations)).markAsRead(blip);
}
public void testMarkAsUnreadClearsAutoReadingButContinuesReadingActiveBlip() {
ConversationBlip other = mockBlip();
target.startReading(blip);
target.markAsRead(other);
reset(supplement);
target.markAsUnread();
verify(supplement, never()).markAsRead(other);
verify(supplement).markAsRead(blip);
}
}