/**
* Copyright 2009 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 net.sf.katta.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import net.sf.katta.AbstractTest;
import net.sf.katta.client.ClientResult.IClosedListener;
import org.junit.Test;
/**
* Test for {@link ClientResult}.
*/
public class ClientResultTest extends AbstractTest {
@Test
public void testToStringResults() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
assertEquals("ClientResult: 0 results, 0 errors, 0/3 shards", r.toString());
r.addResult("x", "a");
assertEquals("ClientResult: 1 results, 0 errors, 1/3 shards", r.toString());
r.addResult("x", "b");
assertEquals("ClientResult: 2 results, 0 errors, 2/3 shards", r.toString());
r.addResult(null, "c");
assertEquals("ClientResult: 2 results, 0 errors, 3/3 shards (complete)", r.toString());
r.close();
assertEquals("ClientResult: 2 results, 0 errors, 3/3 shards (closed) (complete)", r.toString());
}
@Test
public void testToStringErrors() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
assertEquals("ClientResult: 0 results, 0 errors, 0/3 shards", r.toString());
r.addError(new Throwable(""), "a");
assertEquals("ClientResult: 0 results, 1 errors, 1/3 shards", r.toString());
r.addError(new Exception(""), "b");
assertEquals("ClientResult: 0 results, 2 errors, 2/3 shards", r.toString());
r.addError(null, "c");
assertEquals("ClientResult: 0 results, 2 errors, 3/3 shards (complete)", r.toString());
}
@Test
public void testToStringMixed() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
assertEquals("ClientResult: 0 results, 0 errors, 0/3 shards", r.toString());
r.addResult("x", "a");
assertEquals("ClientResult: 1 results, 0 errors, 1/3 shards", r.toString());
r.addResult("x", "b");
assertEquals("ClientResult: 2 results, 0 errors, 2/3 shards", r.toString());
r.addError(new Exception(), "c");
assertEquals("ClientResult: 2 results, 1 errors, 3/3 shards (complete)", r.toString());
}
@Test
public void testResults() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c", "d");
assertEquals("[]", r.getResults().toString());
r.addResult("r1", "a");
assertEquals("[r1]", r.getResults().toString());
r.addError(new Exception("foo"), "b");
assertEquals("[r1]", r.getResults().toString());
r.addResult("r2", "c");
assertEquals(8, r.getResults().toString().length());
assertTrue(r.getResults().toString().indexOf("r1") > 0);
assertTrue(r.getResults().toString().indexOf("r2") > 0);
r.addResult(null, "c");
assertEquals(8, r.getResults().toString().length());
assertTrue(r.getResults().toString().indexOf("r1") > 0);
assertTrue(r.getResults().toString().indexOf("r2") > 0);
}
@Test
public void testDuplicateResults() {
ClientResult<Integer> r = new ClientResult<Integer>(null, "a", "b", "c");
r.addResult(5, "a");
r.addResult(5, "b");
r.addResult(5, "c");
assertEquals(3, r.getResults().size());
assertEquals(3, r.entrySet().size());
r.addResult(5, "a");
assertEquals(4, r.getResults().size());
assertEquals(4, r.entrySet().size());
}
@Test
public void testDuplicateErrors() {
ClientResult<Integer> r = new ClientResult<Integer>(null, "a", "b", "c");
Throwable t = new Throwable();
r.addError(t, "a");
r.addError(t, "b");
r.addError(t, "c");
assertEquals(3, r.getErrors().size());
assertEquals(3, r.entrySet().size());
r.addError(t, "a");
assertEquals(4, r.getErrors().size());
assertEquals(4, r.entrySet().size());
}
@Test
public void testErrors() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c", "d");
assertEquals("[]", r.getErrors().toString());
assertNull(r.getError());
assertFalse(r.isError());
r.addResult("r1", "a");
assertEquals("[]", r.getErrors().toString());
assertFalse(r.isError());
assertNull(r.getError());
r.addError(new OutOfMemoryError("foo"), "b");
assertEquals("[java.lang.OutOfMemoryError: foo]", r.getErrors().toString());
assertEquals("java.lang.OutOfMemoryError: foo", r.getError().toString());
assertTrue(r.isError());
r.addError(new NullPointerException(), "c");
assertEquals(65, r.getErrors().toString().length());
assertTrue(r.getErrors().toString().indexOf("java.lang.OutOfMemoryError: foo") > 0);
assertTrue(r.getErrors().toString().indexOf("java.lang.NullPointerException") > 0);
assertTrue(r.getError() instanceof OutOfMemoryError || r.getError() instanceof NullPointerException);
assertTrue(r.isError());
}
@Test
public void testNoShards() {
try {
new ClientResult<String>((IClosedListener) null, (Collection<String>) null);
fail("Should have thrown an exception");
} catch (IllegalArgumentException e) {
// Good.
}
try {
new ClientResult<String>((IClosedListener) null, new ArrayList<String>());
fail("Should have thrown an exception");
} catch (IllegalArgumentException e) {
// Good.
}
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
r.addResult("foo", (Collection<String>) null);
assertTrue(r.getSeenShards().isEmpty());
assertTrue(r.getResults().isEmpty());
r.addResult("foo", new ArrayList<String>());
assertTrue(r.getSeenShards().isEmpty());
assertTrue(r.getResults().isEmpty());
r.addError(new Exception("foo"), (Collection<String>) null);
assertTrue(r.getSeenShards().isEmpty());
assertTrue(r.getErrors().isEmpty());
r.addError(new Exception("foo"), new ArrayList<String>());
assertTrue(r.getSeenShards().isEmpty());
assertTrue(r.getErrors().isEmpty());
}
@Test
public void testNulls() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
r.addResult(null, "a");
assertEquals(1, r.getSeenShards().size());
assertTrue(r.getSeenShards().contains("a"));
assertTrue(r.getResults().isEmpty());
assertTrue(r.getErrors().isEmpty());
assertFalse(r.isError());
assertEquals(0.3333, r.getShardCoverage(), 0.001);
assertEquals(1, r.getArrivalTimes().size());
assertTrue(r.getArrivalTimes().get(0).toString().startsWith("null from [a] at "));
sleep(3);
r.addError(null, "b");
assertEquals(2, r.getSeenShards().size());
assertTrue(r.getSeenShards().contains("b"));
assertTrue(r.getResults().isEmpty());
assertTrue(r.getErrors().isEmpty());
assertFalse(r.isError());
assertEquals(0.6666, r.getShardCoverage(), 0.001);
assertEquals(2, r.getArrivalTimes().size());
assertTrue(r.getArrivalTimes().get(1).toString().startsWith("null from [b] at "));
sleep(3);
r.addResult(null, "c");
assertEquals(3, r.getSeenShards().size());
assertTrue(r.getSeenShards().contains("c"));
assertTrue(r.getResults().isEmpty());
assertTrue(r.getErrors().isEmpty());
assertFalse(r.isError());
assertEquals(1.0, r.getShardCoverage(), 0.001);
assertTrue(r.getArrivalTimes().get(2).toString().startsWith("null from [c] at "));
assertEquals(3, r.getArrivalTimes().size());
assertTrue(r.isComplete());
assertTrue(r.isOK());
}
protected static class ToStringFails {
@Override
public String toString() {
throw new RuntimeException("err");
}
}
@Test
public void testEntryBadResult() {
ClientResult<ToStringFails> r = new ClientResult<ToStringFails>(null, "a", "b", "c");
r.addResult(new ToStringFails(), "c");
assertTrue(r.entrySet().iterator().next().toString().startsWith("(toString() err) from [c] at "));
r = new ClientResult<ToStringFails>(null, "a", "b", "c");
r.addResult(null, "c");
assertTrue(r.entrySet().iterator().next().toString().startsWith("null from [c] at "));
r = new ClientResult<ToStringFails>(null, "a", "b", "c");
r.addError(null, "c");
assertTrue(r.entrySet().iterator().next().toString().startsWith("null from [c] at "));
}
@Test
public void testMissingAndSeenShardsSingle() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
List<String> missing = new ArrayList<String>(r.getMissingShards());
Collections.sort(missing);
assertEquals("[a, b, c]", missing.toString());
assertTrue(r.getSeenShards().isEmpty());
//
r.addResult("x", "b");
missing = new ArrayList<String>(r.getMissingShards());
Collections.sort(missing);
assertEquals("[a, c]", missing.toString());
List<String> seen = new ArrayList<String>(r.getSeenShards());
Collections.sort(seen);
assertEquals("[b]", seen.toString());
//
r.addError(new Exception(""), "a");
missing = new ArrayList<String>(r.getMissingShards());
Collections.sort(missing);
assertEquals("[c]", missing.toString());
seen = new ArrayList<String>(r.getSeenShards());
Collections.sort(seen);
assertEquals("[a, b]", seen.toString());
//
r.addResult("x", "c");
assertTrue(r.getMissingShards().isEmpty());
seen = new ArrayList<String>(r.getSeenShards());
Collections.sort(seen);
assertEquals("[a, b, c]", seen.toString());
}
@Test
public void testMissingAndSeenShardsMulti() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
r.addResult("x", "a", "b");
List<String> missing = new ArrayList<String>(r.getMissingShards());
Collections.sort(missing);
assertEquals("[c]", missing.toString());
List<String> seen = new ArrayList<String>(r.getSeenShards());
Collections.sort(seen);
assertEquals("[a, b]", seen.toString());
//
r = new ClientResult<String>(null, "a", "b", "c");
r.addResult("x", "a", "b", "c");
assertTrue(r.getMissingShards().isEmpty());
missing = new ArrayList<String>(r.getMissingShards());
Collections.sort(missing);
seen = new ArrayList<String>(r.getSeenShards());
Collections.sort(seen);
assertEquals("[a, b, c]", seen.toString());
}
@Test
public void testUnknownShards() {
// This should not happen normally.
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
assertEquals(3, r.getMissingShards().size());
assertEquals(0, r.getSeenShards().size());
assertEquals(0, r.entrySet().size());
assertFalse(r.isComplete());
r.addResult("foo", "x");
assertEquals(3, r.getMissingShards().size());
assertEquals(1, r.getSeenShards().size());
assertEquals(1, r.entrySet().size());
assertFalse(r.isComplete());
r.addResult("foo", "y");
assertEquals(3, r.getMissingShards().size());
assertEquals(2, r.getSeenShards().size());
assertEquals(2, r.entrySet().size());
assertFalse(r.isComplete());
r.addResult("foo", "z");
assertEquals(3, r.getMissingShards().size());
assertEquals(3, r.getSeenShards().size());
assertEquals(3, r.entrySet().size());
assertFalse(r.isComplete());
r.addResult("foo", "a", "b", "c");
assertEquals(0, r.getMissingShards().size());
assertEquals(6, r.getSeenShards().size());
assertEquals(4, r.entrySet().size());
assertTrue(r.isComplete());
}
@Test
public void testDuplicateShards() {
// This should not happen normally.
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
assertEquals(3, r.getMissingShards().size());
assertEquals(0, r.getSeenShards().size());
assertEquals(0, r.entrySet().size());
assertFalse(r.isComplete());
r.addResult("foo", "a", "b");
assertEquals(1, r.getMissingShards().size());
assertEquals(2, r.getSeenShards().size());
assertEquals(1, r.entrySet().size());
assertFalse(r.isComplete());
r.addResult("foo", "b", "c");
assertEquals(0, r.getMissingShards().size());
assertEquals(3, r.getSeenShards().size());
assertEquals(2, r.entrySet().size());
assertTrue(r.isComplete());
r.addResult("foo", "c", "a");
assertEquals(0, r.getMissingShards().size());
assertEquals(3, r.getSeenShards().size());
assertEquals(3, r.entrySet().size());
assertTrue(r.isComplete());
}
@Test
public void testClosingCallback() {
final AtomicInteger count = new AtomicInteger(0);
ClientResult<String> r = new ClientResult<String>(new IClosedListener() {
public void clientResultClosed() {
count.incrementAndGet();
}
}, "a", "b", "c");
assertEquals(0, count.get());
r.close();
assertEquals(1, count.get());
r.close();
assertEquals(1, count.get());
// Test no listener.
r = new ClientResult<String>(null, "shard");
assertFalse(r.isClosed());
r.close();
assertTrue(r.isClosed());
}
@Test
public void testClosed() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
r.close();
r.addResult("r1", "a");
r.addError(new Exception(), "b", "c");
assertEquals("ClientResult: 0 results, 0 errors, 0/3 shards (closed)", r.toString());
}
@Test
public void testArrivalTimes() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
r.addResult("r1", "a");
sleep(3);
Throwable t = new Throwable();
r.addError(t, "b");
sleep(3);
r.addResult("r2", "c");
List<ClientResult<String>.Entry> entries = r.getArrivalTimes();
assertNotNull(entries);
assertEquals(3, entries.size());
ClientResult<String>.Entry e1 = entries.get(0);
ClientResult<String>.Entry e2 = entries.get(1);
ClientResult<String>.Entry e3 = entries.get(2);
assertEquals("r1", e1.result);
assertEquals(t, e2.error);
assertEquals("r2", e3.result);
assertTrue(e2.time > e1.time);
assertTrue(e3.time > e2.time);
}
@Test
public void testArrivalTimesSorting() {
String result = "foo";
Throwable error = new Exception("bar");
Set<String> shardA = new HashSet<String>();
shardA.add("a");
Set<String> shardB = new HashSet<String>();
shardB.add("b");
for (int i = 0; i < 10000; i++) {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
if (i % 1 == 0) {
r.addResult(result, shardA);
r.addError(error, shardB);
} else {
r.addError(error, shardB);
r.addResult(result, shardA);
}
List<ClientResult<String>.Entry> times = r.getArrivalTimes();
assertEquals(2, times.size());
assertEquals("foo", times.get(0).result);
assertNull(times.get(0).error);
assertEquals("[a]", times.get(0).shards.toString());
assertNull(times.get(1).result);
assertEquals("java.lang.Exception: bar", times.get(1).error.toString());
assertEquals("[b]", times.get(1).shards.toString());
}
}
@Test
public void testMultithreaded() throws InterruptedException {
Set<String> shards = new HashSet<String>();
final int size = 1000;
for (int i = 0; i < size; i++) {
shards.add("s" + i);
}
final ClientResult<Integer> r = new ClientResult<Integer>(null, shards);
Random rand = new Random("testMultithreaded".hashCode());
ExecutorService executor = Executors.newFixedThreadPool(15);
int total = 0;
for (int i = 0; i < size; i++) {
final String shard = "s" + i;
final int result = i;
total += result;
final long delay = rand.nextInt(50);
executor.submit(new Runnable() {
public void run() {
sleep(delay);
r.addResult(result, shard);
}
});
}
executor.shutdown();
executor.awaitTermination(3, TimeUnit.MINUTES);
r.close();
//
assertEquals(String
.format("ClientResult: %s results, 0 errors, %d/%d shards (closed) (complete)", size, size, size), r
.toString());
int resultTotal = 0;
for (ClientResult<Integer>.Entry e : r) {
resultTotal += e.result;
}
assertEquals(total, resultTotal);
}
@Test
public void testIteratorEmpty() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
assertFalse(r.iterator().hasNext());
}
@Test
public void testIteratorWhileAdding() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
r.addResult("r1", "a");
r.addResult("r2", "b");
Iterator<ClientResult<String>.Entry> i = r.iterator();
r.addResult("r3", "c");
assertTrue(i.hasNext());
ClientResult<String>.Entry e1 = i.next();
assertTrue(e1.result.equals("r1") || e1.result.equals("r2"));
assertTrue(e1.shards.size() == 1);
assertTrue(e1.shards.iterator().next().equals("a") || e1.shards.iterator().next().equals("b"));
ClientResult<String>.Entry e2 = i.next();
assertTrue(e2.result.equals("r1") || e2.result.equals("r2"));
assertTrue(e2.shards.size() == 1);
assertTrue(e2.shards.iterator().next().equals("a") || e2.shards.iterator().next().equals("b"));
assertFalse(e1.result.equals(e2.result));
assertFalse(e1.shards.iterator().next().equals(e2.shards.iterator().next()));
assertFalse(i.hasNext());
}
@Test
public void testReadOnly() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c");
checkReadOnly(r);
r.addResult("r1", "a");
checkReadOnly(r);
r.addError(new Exception(), "b");
checkReadOnly(r);
r.addResult("r3", "c");
checkReadOnly(r);
r.close();
checkReadOnly(r);
}
private void checkReadOnly(ClientResult<String> r) {
checkCollectionReadOnly(r.getAllShards());
checkCollectionReadOnly(r.entrySet());
checkCollectionReadOnly(r.getResults());
checkCollectionReadOnly(r.getErrors());
checkCollectionReadOnly(r.getSeenShards());
try {
r.iterator().remove();
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
}
@SuppressWarnings("unchecked")
private void checkCollectionReadOnly(Collection<?> s) {
try {
s.remove(0);
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
try {
s.clear();
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
if (!s.isEmpty()) {
try {
s.remove(s.iterator().next());
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
}
try {
s.removeAll(new ArrayList<ClientResult<String>.Entry>());
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
try {
s.retainAll(new ArrayList<ClientResult<String>.Entry>());
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
try {
s.add(null);
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
try {
s.addAll(new ArrayList());
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
try {
s.iterator().remove();
fail("Should be read only");
} catch (UnsupportedOperationException e) {
// expected
}
}
@Test
public void testCoverage() {
ClientResult<String> r = new ClientResult<String>(null, "a", "b", "c", "d", "e");
assertEquals(0.0, r.getShardCoverage(), 0.000001);
assertFalse(r.isComplete());
assertFalse(r.isOK());
r.addResult("r1", "a");
assertEquals(0.2, r.getShardCoverage(), 0.000001);
assertFalse(r.isComplete());
assertFalse(r.isOK());
r.addResult("r1", "b");
assertEquals(0.4, r.getShardCoverage(), 0.000001);
assertFalse(r.isComplete());
assertFalse(r.isOK());
r.addResult("r1", "c", "d");
assertEquals(0.8, r.getShardCoverage(), 0.000001);
assertFalse(r.isComplete());
assertFalse(r.isOK());
r.addResult("r1", "e");
assertTrue(r.isComplete());
assertTrue(r.isOK());
assertEquals(1.0, r.getShardCoverage(), 0.000001);
}
@Test
public void testStartTime() {
ClientResult<String> r1 = new ClientResult<String>(null, "a", "b", "c");
sleep(10);
ClientResult<String> r2 = new ClientResult<String>(null, "a", "b", "c");
assertTrue(r2.getStartTime() - r1.getStartTime() >= 10);
}
protected void sleep(long msec) {
long now = System.currentTimeMillis();
long waitUntil = now + msec;
while (now < waitUntil) {
long remainingTime = waitUntil - now;
try {
Thread.sleep(remainingTime);
} catch (InterruptedException e) {
// ignore and continue waiting
}
now = System.currentTimeMillis();
}
}
}