/* * Copyright (c) 2008-2017 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.cometd.oort; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.junit.Assert; import org.junit.Test; public class OortListTest extends AbstractOortObjectTest { public OortListTest(String serverTransport) { super(serverTransport); } @Test public void testElementAdded() throws Exception { String name = "test"; OortObject.Factory<List<Long>> factory = OortObjectFactories.forConcurrentList(); OortList<Long> oortList1 = new OortList<>(oort1, name, factory); OortList<Long> oortList2 = new OortList<>(oort2, name, factory); startOortObjects(oortList1, oortList2); final long element = 1; final CountDownLatch addLatch = new CountDownLatch(1); oortList2.addElementListener(new OortList.ElementListener.Adapter<Long>() { @Override public void onAdded(OortObject.Info<List<Long>> info, List<Long> elements) { Assert.assertEquals(1, elements.size()); Assert.assertEquals(element, (long)elements.get(0)); addLatch.countDown(); } }); OortObject.Result.Deferred<Boolean> result = new OortObject.Result.Deferred<>(); oortList1.addAndShare(result, element); Assert.assertTrue(result.get(5, TimeUnit.SECONDS)); Assert.assertTrue(addLatch.await(5, TimeUnit.SECONDS)); } @Test public void testElementRemoved() throws Exception { String name = "test"; OortObject.Factory<List<Long>> factory = OortObjectFactories.forConcurrentList(); OortList<Long> oortList1 = new OortList<>(oort1, name, factory); OortList<Long> oortList2 = new OortList<>(oort2, name, factory); startOortObjects(oortList1, oortList2); final CountDownLatch setLatch = new CountDownLatch(2); OortObject.Listener<List<Long>> listener = new OortObject.Listener.Adapter<List<Long>>() { @Override public void onUpdated(OortObject.Info<List<Long>> oldInfo, OortObject.Info<List<Long>> newInfo) { setLatch.countDown(); } }; oortList1.addListener(listener); oortList2.addListener(listener); List<Long> list = factory.newObject(null); final long element = 1; list.add(element); oortList1.setAndShare(list, null); Assert.assertTrue(setLatch.await(5, TimeUnit.SECONDS)); final CountDownLatch removeLatch = new CountDownLatch(1); oortList2.addElementListener(new OortList.ElementListener.Adapter<Long>() { @Override public void onRemoved(OortObject.Info<List<Long>> info, List<Long> elements) { Assert.assertEquals(1, elements.size()); Assert.assertEquals(element, (long)elements.get(0)); removeLatch.countDown(); } }); OortObject.Result.Deferred<Boolean> result = new OortObject.Result.Deferred<>(); oortList1.removeAndShare(result, element); Assert.assertTrue(result.get(5, TimeUnit.SECONDS)); Assert.assertTrue(removeLatch.await(5, TimeUnit.SECONDS)); } @Test public void testDeltaListener() throws Exception { String name = "test"; OortObject.Factory<List<String>> factory = OortObjectFactories.forConcurrentList(); OortList<String> oortList1 = new OortList<>(oort1, name, factory); OortList<String> oortList2 = new OortList<>(oort2, name, factory); startOortObjects(oortList1, oortList2); final CountDownLatch setLatch1 = new CountDownLatch(2); OortObject.Listener<List<String>> listener = new OortObject.Listener.Adapter<List<String>>() { @Override public void onUpdated(OortObject.Info<List<String>> oldInfo, OortObject.Info<List<String>> newInfo) { setLatch1.countDown(); } }; oortList1.addListener(listener); oortList2.addListener(listener); List<String> oldList = factory.newObject(null); String elementA = "A"; String elementB = "B"; oldList.add(elementA); oldList.add(elementB); oortList1.setAndShare(oldList, null); Assert.assertTrue(setLatch1.await(5, TimeUnit.SECONDS)); List<String> newList = factory.newObject(null); String elementC = "C"; String elementD = "D"; newList.add(elementA); newList.add(elementC); newList.add(elementD); final List<String> adds = new ArrayList<>(); final List<String> removes = new ArrayList<>(); final AtomicReference<CountDownLatch> setLatch2 = new AtomicReference<>(new CountDownLatch(4)); oortList1.addListener(new OortList.DeltaListener<>(oortList1)); oortList2.addListener(new OortList.DeltaListener<>(oortList2)); OortList.ElementListener<String> elementListener = new OortList.ElementListener<String>() { @Override public void onAdded(OortObject.Info<List<String>> info, List<String> elements) { adds.addAll(elements); setLatch2.get().countDown(); } @Override public void onRemoved(OortObject.Info<List<String>> info, List<String> elements) { removes.addAll(elements); setLatch2.get().countDown(); } }; oortList1.addElementListener(elementListener); oortList2.addElementListener(elementListener); oortList1.setAndShare(newList, null); Assert.assertTrue(setLatch2.get().await(5, TimeUnit.SECONDS)); Assert.assertEquals(4, adds.size()); Assert.assertEquals(2, removes.size()); adds.clear(); removes.clear(); setLatch2.set(new CountDownLatch(1)); // Stop Oort1 so that OortList2 gets the notification stopOort(oort1); Assert.assertTrue(setLatch2.get().await(5, TimeUnit.SECONDS)); Assert.assertEquals(3, removes.size()); } @Test public void testContains() throws Exception { String name = "test"; OortObject.Factory<List<String>> factory = OortObjectFactories.forConcurrentList(); OortList<String> oortList1 = new OortList<>(oort1, name, factory); OortList<String> oortList2 = new OortList<>(oort2, name, factory); startOortObjects(oortList1, oortList2); final CountDownLatch setLatch = new CountDownLatch(2); OortObject.Listener<List<String>> listener = new OortObject.Listener.Adapter<List<String>>() { @Override public void onUpdated(OortObject.Info<List<String>> oldInfo, OortObject.Info<List<String>> newInfo) { setLatch.countDown(); } }; oortList1.addListener(listener); oortList2.addListener(listener); oortList1.setAndShare(factory.newObject(null), null); Assert.assertTrue(setLatch.await(5, TimeUnit.SECONDS)); final CountDownLatch addLatch = new CountDownLatch(4); OortList.ElementListener<String> addedListener = new OortList.ElementListener.Adapter<String>() { @Override public void onAdded(OortObject.Info<List<String>> info, List<String> elements) { addLatch.countDown(); } }; oortList1.addElementListener(addedListener); oortList2.addElementListener(addedListener); OortObject.Result<Boolean> nullResult = null; String element1A = "1A"; oortList1.addAndShare(nullResult, element1A); String element2A = "2A"; oortList2.addAndShare(nullResult, element2A); Assert.assertTrue(addLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(oortList1.contains(element1A)); Assert.assertFalse(oortList2.contains(element1A)); Assert.assertFalse(oortList1.contains(element2A)); Assert.assertTrue(oortList2.contains(element2A)); Assert.assertTrue(oortList1.isPresent(element1A)); Assert.assertTrue(oortList2.isPresent(element1A)); Assert.assertTrue(oortList1.isPresent(element2A)); Assert.assertTrue(oortList2.isPresent(element2A)); oortList2.removeElementListener(addedListener); oortList1.removeElementListener(addedListener); } @Test public void testConcurrent() throws Exception { String name = "concurrent"; OortObject.Factory<List<String>> factory = OortObjectFactories.forConcurrentList(); final OortList<String> oortList1 = new OortList<>(oort1, name, factory); OortList<String> oortList2 = new OortList<>(oort2, name, factory); startOortObjects(oortList1, oortList2); int threads = 64; final int iterations = 32; final CyclicBarrier barrier = new CyclicBarrier(threads + 1); final CountDownLatch latch1 = new CountDownLatch(threads); final CountDownLatch latch2 = new CountDownLatch(threads * iterations); oortList2.addListener(new OortList.DeltaListener<>(oortList2)); oortList2.addElementListener(new OortList.ElementListener.Adapter<String>() { @Override public void onAdded(OortObject.Info<List<String>> info, List<String> elements) { for (int i = 0; i < elements.size(); ++i) { latch2.countDown(); } } }); for (int i = 0; i < threads; ++i) { final int index = i; new Thread(new Runnable() { @Override public void run() { try { barrier.await(); for (int j = 0; j < iterations; ++j) { String element = String.valueOf(index * iterations + j); oortList1.addAndShare((OortObject.Result<Boolean>)null, element); } } catch (Throwable x) { x.printStackTrace(); } finally { latch1.countDown(); } } }).start(); } // Wait for all threads to be ready. barrier.await(); // Wait for all threads to finish. Assert.assertTrue(latch1.await(5, TimeUnit.SECONDS)); Assert.assertTrue(latch2.await(5, TimeUnit.SECONDS)); List<String> list1 = oortList1.merge(OortObjectMergers.<String>listUnion()); Collections.sort(list1); List<String> list2 = oortList2.merge(OortObjectMergers.<String>listUnion()); Collections.sort(list2); Assert.assertEquals(list1, list2); } }