+/*
+ * Copyright 2005-2010 the original author or authors.
+ *
+ * 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.wamblee.cache;
+
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.wamblee.cache.ComputedValue.Computation;
+
+public class ComputedValueTest extends TestCase {
+
+ private Computation<Integer> computation;
+ private ComputedValue<Integer> guard;
+
+
+ @Override
+ protected void setUp() throws Exception {
+ computation = mock(Computation.class);
+ }
+
+ public void testComputeOutOfDate() {
+ initGuard();
+ }
+
+ private void initGuard() {
+ guard = new ComputedValue<Integer>(this, computation);
+
+ assertNull(guard.getCached());
+
+ when(computation.isOutOfDate()).thenReturn(true);
+ when(computation.compute()).thenReturn(10);
+
+ int value = guard.get();
+ assertEquals(10, value);
+ verify(computation).compute();
+ reset(computation);
+ }
+
+ public void testGetCached() {
+ initGuard();
+ assertEquals(10, (int)guard.getCached());
+ verifyNoMoreInteractions(computation);
+ }
+
+ public void testNoComputationWhenNotOutOfDate() {
+ initGuard();
+ when(computation.isOutOfDate()).thenReturn(false);
+ assertEquals(10, (int) guard.get());
+ verify(computation, never()).compute();
+ }
+
+ public void testOnlyOneConcurrentComputation() throws Exception {
+ initGuard();
+ final int computeTime = 500;
+ when(computation.isOutOfDate()).thenReturn(true);
+ when(computation.compute()).thenAnswer(new Answer<Integer>() {
+ @Override
+ public Integer answer(InvocationOnMock aInvocation)
+ throws Throwable {
+ Thread.sleep(computeTime);
+ return 100;
+ }
+ });
+
+ final List<Integer> results = new ArrayList<Integer>();
+
+ Runnable task = new Runnable() {
+ @Override
+ public void run() {
+ int res = guard.get();
+ results.add(res);
+ }
+ };
+
+ // concurrent computation in two threads.
+ Thread t1 = new Thread(task);
+ Thread t2 = new Thread(task);
+ t1.start();
+ Thread.sleep(computeTime / 2);
+ // second task will return old value 10 because first one is still busy.
+ t2.start();
+ t1.join();
+ t2.join();
+ assertEquals(2, results.size());
+ assertEquals(10, (int) results.get(0));
+ assertEquals(100, (int) results.get(1));
+ verify(computation, times(2)).isOutOfDate();
+ }
+
+ public void testExceptionWhileComputing() {
+ initGuard();
+ when(computation.isOutOfDate()).thenReturn(true);
+ when(computation.compute()).thenThrow(new RuntimeException("xx"));
+ try {
+ guard.get();
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals("xx", e.getMessage());
+ }
+ }
+
+ public void testExceptionWhileCheckingOutOfDate() {
+ initGuard();
+ when(computation.isOutOfDate()).thenThrow(new RuntimeException("xx"));
+ try {
+ guard.get();
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals("xx", e.getMessage());
+ }
+ }
+}