| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 storm.starter.tools; |
| |
| import com.google.common.base.Throwables; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| import org.jmock.lib.concurrent.Blitzer; |
| import org.testng.annotations.DataProvider; |
| import org.testng.annotations.Test; |
| |
| import java.util.List; |
| |
| import static org.fest.assertions.api.Assertions.assertThat; |
| |
| public class RankingsTest { |
| |
| private static final int ANY_TOPN = 42; |
| private static final Rankable ANY_RANKABLE = new RankableObjectWithFields("someObject", ANY_TOPN); |
| private static final Rankable ZERO = new RankableObjectWithFields("ZERO_COUNT", 0); |
| private static final Rankable A = new RankableObjectWithFields("A", 1); |
| private static final Rankable B = new RankableObjectWithFields("B", 2); |
| private static final Rankable C = new RankableObjectWithFields("C", 3); |
| private static final Rankable D = new RankableObjectWithFields("D", 4); |
| private static final Rankable E = new RankableObjectWithFields("E", 5); |
| private static final Rankable F = new RankableObjectWithFields("F", 6); |
| private static final Rankable G = new RankableObjectWithFields("G", 7); |
| private static final Rankable H = new RankableObjectWithFields("H", 8); |
| |
| @DataProvider |
| public Object[][] illegalTopNData() { |
| return new Object[][]{ { 0 }, { -1 }, { -2 }, { -10 } }; |
| } |
| |
| @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "illegalTopNData") |
| public void constructorWithNegativeOrZeroTopNShouldThrowIAE(int topN) { |
| new Rankings(topN); |
| } |
| |
| @DataProvider |
| public Object[][] copyRankingsData() { |
| return new Object[][]{ { 5, Lists.newArrayList(A, B, C) }, { 2, Lists.newArrayList(A, B, C, D) }, |
| { 1, Lists.newArrayList() }, { 1, Lists.newArrayList(A) }, { 1, Lists.newArrayList(A, B) } }; |
| } |
| |
| @Test(dataProvider = "copyRankingsData") |
| public void copyConstructorShouldReturnCopy(int topN, List<Rankable> rankables) { |
| // given |
| Rankings rankings = new Rankings(topN); |
| for (Rankable r : rankables) { |
| rankings.updateWith(r); |
| } |
| |
| // when |
| Rankings copy = new Rankings(rankings); |
| |
| // then |
| assertThat(copy.maxSize()).isEqualTo(rankings.maxSize()); |
| assertThat(copy.getRankings()).isEqualTo(rankings.getRankings()); |
| } |
| |
| @DataProvider |
| public Object[][] defensiveCopyRankingsData() { |
| return new Object[][]{ { 5, Lists.newArrayList(A, B, C), Lists.newArrayList(D) }, { 2, Lists.newArrayList(A, B, C, |
| D), Lists.newArrayList(E, F) }, { 1, Lists.newArrayList(), Lists.newArrayList(A) }, { 1, Lists.newArrayList(A), |
| Lists.newArrayList(B) }, { 1, Lists.newArrayList(ZERO), Lists.newArrayList(B) }, { 1, Lists.newArrayList(ZERO), |
| Lists.newArrayList() } }; |
| } |
| |
| @Test(dataProvider = "defensiveCopyRankingsData") |
| public void copyConstructorShouldReturnDefensiveCopy(int topN, List<Rankable> rankables, List<Rankable> changes) { |
| // given |
| Rankings original = new Rankings(topN); |
| for (Rankable r : rankables) { |
| original.updateWith(r); |
| } |
| int expSize = original.size(); |
| List<Rankable> expRankings = original.getRankings(); |
| |
| // when |
| Rankings copy = new Rankings(original); |
| for (Rankable r : changes) { |
| copy.updateWith(r); |
| } |
| |
| // then |
| assertThat(original.size()).isEqualTo(expSize); |
| assertThat(original.getRankings()).isEqualTo(expRankings); |
| } |
| |
| @DataProvider |
| public Object[][] legalTopNData() { |
| return new Object[][]{ { 1 }, { 2 }, { 1000 }, { 1000000 } }; |
| } |
| |
| @Test(dataProvider = "legalTopNData") |
| public void constructorWithPositiveTopNShouldBeOk(int topN) { |
| // given/when |
| Rankings rankings = new Rankings(topN); |
| |
| // then |
| assertThat(rankings.maxSize()).isEqualTo(topN); |
| } |
| |
| @Test |
| public void shouldHaveDefaultConstructor() { |
| new Rankings(); |
| } |
| |
| @Test |
| public void defaultConstructorShouldSetPositiveTopN() { |
| // given/when |
| Rankings rankings = new Rankings(); |
| |
| // then |
| assertThat(rankings.maxSize()).isGreaterThan(0); |
| } |
| |
| @DataProvider |
| public Object[][] rankingsGrowData() { |
| return new Object[][]{ { 2, Lists.newArrayList(new RankableObjectWithFields("A", 1), new RankableObjectWithFields( |
| "B", 2), new RankableObjectWithFields("C", 3)) }, { 2, Lists.newArrayList(new RankableObjectWithFields("A", 1), |
| new RankableObjectWithFields("B", 2), new RankableObjectWithFields("C", 3), new RankableObjectWithFields("D", |
| 4)) } }; |
| } |
| |
| @Test(dataProvider = "rankingsGrowData") |
| public void sizeOfRankingsShouldNotGrowBeyondTopN(int topN, List<Rankable> rankables) { |
| // sanity check of the provided test data |
| assertThat(rankables.size()).overridingErrorMessage( |
| "The supplied test data is not correct: the number of rankables <%d> should be greater than <%d>", |
| rankables.size(), topN).isGreaterThan(topN); |
| |
| // given |
| Rankings rankings = new Rankings(topN); |
| |
| // when |
| for (Rankable r : rankables) { |
| rankings.updateWith(r); |
| } |
| |
| // then |
| assertThat(rankings.size()).isLessThanOrEqualTo(rankings.maxSize()); |
| } |
| |
| @DataProvider |
| public Object[][] simulatedRankingsData() { |
| return new Object[][]{ { Lists.newArrayList(A), Lists.newArrayList(A) }, { Lists.newArrayList(B, D, A, C), |
| Lists.newArrayList(D, C, B, A) }, { Lists.newArrayList(B, F, A, C, D, E), Lists.newArrayList(F, E, D, C, B, |
| A) }, { Lists.newArrayList(G, B, F, A, C, D, E, H), Lists.newArrayList(H, G, F, E, D, C, B, A) } }; |
| } |
| |
| @Test(dataProvider = "simulatedRankingsData") |
| public void shouldCorrectlyRankWhenUpdatedWithRankables(List<Rankable> unsorted, List<Rankable> expSorted) { |
| // given |
| Rankings rankings = new Rankings(unsorted.size()); |
| |
| // when |
| for (Rankable r : unsorted) { |
| rankings.updateWith(r); |
| } |
| |
| // then |
| assertThat(rankings.getRankings()).isEqualTo(expSorted); |
| } |
| |
| @Test(dataProvider = "simulatedRankingsData") |
| public void shouldCorrectlyRankWhenEmptyAndUpdatedWithOtherRankings(List<Rankable> unsorted, |
| List<Rankable> expSorted) { |
| // given |
| Rankings rankings = new Rankings(unsorted.size()); |
| Rankings otherRankings = new Rankings(rankings.maxSize()); |
| for (Rankable r : unsorted) { |
| otherRankings.updateWith(r); |
| } |
| |
| // when |
| rankings.updateWith(otherRankings); |
| |
| // then |
| assertThat(rankings.getRankings()).isEqualTo(expSorted); |
| } |
| |
| @Test(dataProvider = "simulatedRankingsData") |
| public void shouldCorrectlyRankWhenUpdatedWithEmptyOtherRankings(List<Rankable> unsorted, List<Rankable> expSorted) { |
| // given |
| Rankings rankings = new Rankings(unsorted.size()); |
| for (Rankable r : unsorted) { |
| rankings.updateWith(r); |
| } |
| Rankings emptyRankings = new Rankings(ANY_TOPN); |
| |
| // when |
| rankings.updateWith(emptyRankings); |
| |
| // then |
| assertThat(rankings.getRankings()).isEqualTo(expSorted); |
| } |
| |
| @DataProvider |
| public Object[][] simulatedRankingsAndOtherRankingsData() { |
| return new Object[][]{ { Lists.newArrayList(A), Lists.newArrayList(A), Lists.newArrayList(A) }, |
| { Lists.newArrayList(A, C), Lists.newArrayList(B, D), Lists.newArrayList(D, C, B, A) }, { Lists.newArrayList(B, |
| F, A), Lists.newArrayList(C, D, E), Lists.newArrayList(F, E, D, C, B, A) }, { Lists.newArrayList(G, B, F, A, C), |
| Lists.newArrayList(D, E, H), Lists.newArrayList(H, G, F, E, D, C, B, A) } }; |
| } |
| |
| @Test(dataProvider = "simulatedRankingsAndOtherRankingsData") |
| public void shouldCorrectlyRankWhenNotEmptyAndUpdatedWithOtherRankings(List<Rankable> unsorted, |
| List<Rankable> unsortedForOtherRankings, List<Rankable> expSorted) { |
| // given |
| Rankings rankings = new Rankings(expSorted.size()); |
| for (Rankable r : unsorted) { |
| rankings.updateWith(r); |
| } |
| Rankings otherRankings = new Rankings(unsortedForOtherRankings.size()); |
| for (Rankable r : unsortedForOtherRankings) { |
| otherRankings.updateWith(r); |
| } |
| |
| // when |
| rankings.updateWith(otherRankings); |
| |
| // then |
| assertThat(rankings.getRankings()).isEqualTo(expSorted); |
| } |
| |
| @DataProvider |
| public Object[][] duplicatesData() { |
| Rankable A1 = new RankableObjectWithFields("A", 1); |
| Rankable A2 = new RankableObjectWithFields("A", 2); |
| Rankable A3 = new RankableObjectWithFields("A", 3); |
| return new Object[][]{ { Lists.newArrayList(ANY_RANKABLE, ANY_RANKABLE, ANY_RANKABLE) }, { Lists.newArrayList(A1, |
| A2, A3) }, }; |
| } |
| |
| @Test(dataProvider = "duplicatesData") |
| public void shouldNotRankDuplicateObjectsMoreThanOnce(List<Rankable> duplicates) { |
| // given |
| Rankings rankings = new Rankings(duplicates.size()); |
| |
| // when |
| for (Rankable r : duplicates) { |
| rankings.updateWith(r); |
| } |
| |
| // then |
| assertThat(rankings.size()).isEqualTo(1); |
| } |
| |
| @DataProvider |
| public Object[][] removeZeroRankingsData() { |
| return new Object[][]{ { Lists.newArrayList(A, ZERO), Lists.newArrayList(A) }, { Lists.newArrayList(A), |
| Lists.newArrayList(A) }, { Lists.newArrayList(ZERO, A), Lists.newArrayList(A) }, { Lists.newArrayList(ZERO), |
| Lists.newArrayList() }, { Lists.newArrayList(ZERO, new RankableObjectWithFields("ZERO2", 0)), |
| Lists.newArrayList() }, { Lists.newArrayList(B, ZERO, new RankableObjectWithFields("ZERO2", 0), D, |
| new RankableObjectWithFields("ZERO3", 0), new RankableObjectWithFields("ZERO4", 0), C), Lists.newArrayList(D, C, |
| B) }, { Lists.newArrayList(A, ZERO, B), Lists.newArrayList(B, A) } }; |
| } |
| |
| @Test(dataProvider = "removeZeroRankingsData") |
| public void shouldRemoveZeroCounts(List<Rankable> unsorted, List<Rankable> expSorted) { |
| // given |
| Rankings rankings = new Rankings(unsorted.size()); |
| for (Rankable r : unsorted) { |
| rankings.updateWith(r); |
| } |
| |
| // when |
| rankings.pruneZeroCounts(); |
| |
| // then |
| assertThat(rankings.getRankings()).isEqualTo(expSorted); |
| } |
| |
| @Test |
| public void updatingWithNewRankablesShouldBeThreadSafe() throws InterruptedException { |
| // given |
| final List<Rankable> entries = ImmutableList.of(A, B, C, D); |
| final Rankings rankings = new Rankings(entries.size()); |
| |
| // We are capturing exceptions thrown in Blitzer's child threads into this data structure so that we can properly |
| // pass/fail this test. The reason is that Blitzer doesn't report exceptions, which is a known bug in Blitzer |
| // (JMOCK-263). See https://github.com/jmock-developers/jmock-library/issues/22 for more information. |
| final List<Exception> exceptions = Lists.newArrayList(); |
| Blitzer blitzer = new Blitzer(1000); |
| |
| // when |
| blitzer.blitz(new Runnable() { |
| public void run() { |
| for (Rankable r : entries) { |
| try { |
| rankings.updateWith(r); |
| } |
| catch (RuntimeException e) { |
| synchronized(exceptions) { |
| exceptions.add(e); |
| } |
| } |
| } |
| } |
| }); |
| blitzer.shutdown(); |
| |
| // then |
| // |
| if (!exceptions.isEmpty()) { |
| for (Exception e : exceptions) { |
| System.err.println(Throwables.getStackTraceAsString(e)); |
| } |
| } |
| assertThat(exceptions).isEmpty(); |
| } |
| |
| @Test(dataProvider = "copyRankingsData") |
| public void copyShouldReturnCopy(int topN, List<Rankable> rankables) { |
| // given |
| Rankings rankings = new Rankings(topN); |
| for (Rankable r : rankables) { |
| rankings.updateWith(r); |
| } |
| |
| // when |
| Rankings copy = rankings.copy(); |
| |
| // then |
| assertThat(copy.maxSize()).isEqualTo(rankings.maxSize()); |
| assertThat(copy.getRankings()).isEqualTo(rankings.getRankings()); |
| } |
| |
| @Test(dataProvider = "defensiveCopyRankingsData") |
| public void copyShouldReturnDefensiveCopy(int topN, List<Rankable> rankables, List<Rankable> changes) { |
| // given |
| Rankings original = new Rankings(topN); |
| for (Rankable r : rankables) { |
| original.updateWith(r); |
| } |
| int expSize = original.size(); |
| List<Rankable> expRankings = original.getRankings(); |
| |
| // when |
| Rankings copy = original.copy(); |
| for (Rankable r : changes) { |
| copy.updateWith(r); |
| } |
| copy.pruneZeroCounts(); |
| |
| // then |
| assertThat(original.size()).isEqualTo(expSize); |
| assertThat(original.getRankings()).isEqualTo(expRankings); |
| } |
| |
| } |