/* * Carrot2 project. * * Copyright (C) 2002-2016, Dawid Weiss, Stanisław Osiński. * All rights reserved. * * Refer to the full license file "carrot2.LICENSE" * in the root folder of the repository checkout or at: * http://www.carrot2.org/carrot2.LICENSE */ package org.carrot2.core.test.assertions; import static org.carrot2.core.test.assertions.Carrot2CoreAssertions.assertThat; import static org.carrot2.core.test.assertions.Carrot2CoreAssertions.assertThatClusters; import static org.fest.assertions.Assertions.assertThat; import java.util.Iterator; import java.util.List; import java.util.Set; import org.carrot2.core.Cluster; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.carrot2.shaded.guava.common.base.Strings; import org.carrot2.shaded.guava.common.collect.Lists; import org.carrot2.shaded.guava.common.collect.Sets; /** * Assertions on lists of {@link Cluster}s. */ public class ClusterListAssertion extends GenericListAssertion<ClusterListAssertion, Cluster> { ClusterListAssertion(List<Cluster> actualClusterList) { super(ClusterListAssertion.class, actualClusterList); } /** * Asserts that the cluster list is equivalent to the provided cluster list. Two lists * of clusters are equivalent if they have the same size, and the clusters on the * corresponding positions on the lists are equivalent (see * {@link ClusterAssertion#isEquivalentTo(Cluster)}. * * @param expectedClusterList the expected cluster list * @return this assertion for convenience */ public ClusterListAssertion isEquivalentTo(List<Cluster> expectedClusterList) { return isEquivalentTo(expectedClusterList, true); } /** * Asserts that the cluster list is equivalent to the provided cluster list. Two lists * of clusters are equivalent if they have the same size, and the clusters on the * corresponding positions on the lists are equivalent (see * {@link ClusterAssertion#isEquivalentTo(Cluster, boolean)}. * * @param expectedClusterList the expected cluster list * @param checkDocuments if <code>false</code>, cluster's document references will not * be checked * @return this assertion for convenience */ public ClusterListAssertion isEquivalentTo(List<Cluster> expectedClusterList, boolean checkDocuments) { assertThat(actual).hasSize(expectedClusterList.size()); for (int i = 0; i < actual.size(); i++) { assertThat(actual.get(i)).isEquivalentTo(expectedClusterList.get(i), checkDocuments); } return this; } /** * Asserts that there are no Other Topics clusters on the provided list. * * @return this assertion for convenience */ public ClusterListAssertion doesNotContainOtherTopicsClusters() { for (int i = 0; i < actual.size(); i++) { assertThat(actual.get(i)).isOtherTopics(false); } return this; } /** * Recursively runs pairwise cluster checks on the actual clusters vs the provided * expected clusters hierarchy. */ public ClusterListAssertion passRecursively(List<Cluster> expected, ClusterPairCheck... clusterAsserts) { if (expected == null) { assertThat(actual).isNull(); } else { assertThat(actual).isNotNull(); } List<String> expectedLabels = labelList(Lists.<String>newArrayList(), 0, expected); List<String> actualLabels = labelList(Lists.<String>newArrayList(), 0, actual); if (!actualLabels.equals(expectedLabels)) { tabularizedReport(expectedLabels, actualLabels); } assertThat(actualLabels).describedAs("Cluster labels").isEqualTo(expectedLabels); Iterator<Cluster> expectedIt = expected.iterator(); Iterator<Cluster> actualIt = actual.iterator(); while (expectedIt.hasNext() && actualIt.hasNext()) { final Cluster actualCluster = actualIt.next(); final Cluster expectedCluster = expectedIt.next(); for (ClusterPairCheck clusterPairAssert : clusterAsserts) { clusterPairAssert.check(actualCluster, expectedCluster); } assertThatClusters(actualCluster.getSubclusters()).as( description() + ": subclusters of \"" + actualCluster.getLabel() + "\"") .passRecursively(expectedCluster.getSubclusters(), clusterAsserts); } return this; } private void tabularizedReport(List<String> l1, List<String> l2) { Logger logger = LoggerFactory.getLogger(ClusterListAssertion.class); int maxL1Width = 0; for (String s : l1) maxL1Width = Math.max(maxL1Width, s.length()); final StringBuilder sb = new StringBuilder(); final Set<String> l1s = Sets.newTreeSet(l1); final Set<String> l2s = Sets.newTreeSet(l2); if (l1s.equals(l2s)) { sb.append("PROBLEM: Same sets different order or hierarchy.\n"); sb.append("Clusters side-by-side (same-line order changes marked):\n"); for (int i = 0; i < Math.max(l1.size(), l2.size()); i++) { String lbl = l1.get(i); sb.append(l2.get(i).equals(lbl) ? " " : "* "); sb.append(Strings.padEnd(lbl, maxL1Width, ' ')); sb.append(" | "); lbl = l2.get(i); sb.append(l1.get(i).equals(lbl) ? " " : "* "); sb.append(lbl); sb.append("\n"); } } else { Set<String> common = Sets.newTreeSet(l1s); common.retainAll(l2s); l1s.removeAll(common); l2s.removeAll(common); sb.append("Clusters in the previous set only:\n"); for (String s : l1s) sb.append(" '" + s + "'\n"); sb.append("Clusters in the actual set only:\n"); for (String s : l2s) sb.append(" '" + s + "'\n"); sb.append("Clusters side-by-side (order changes not shown):\n"); for (int i = 0; i < Math.max(l1.size(), l2.size()); i++) { String lbl = (i < l1.size() ? l1.get(i) : "--"); sb.append(l1s.contains(lbl) ? "* " : " "); sb.append(Strings.padEnd(lbl, maxL1Width, ' ')); sb.append(" | "); lbl = (i < l2.size() ? l2.get(i) : "--"); sb.append(l2s.contains(lbl) ? "* " : " "); sb.append(lbl); sb.append("\n"); } } logger.error("Failed cluster list comparison (previous | now):\n" + sb.toString()); } private List<String> labelList(List<String> list, int indent, List<Cluster> clusters) { for (Cluster c : clusters) { list.add(Strings.repeat(" ", indent) + c.getLabel()); labelList(list, indent + 1, c.getSubclusters()); } return list; } /** * Asserts that the provided checks pass for all clusters on the list, including their * subclusters. */ public ClusterListAssertion passRecursively(ClusterCheck... checks) { for (Cluster cluster : actual) { for (ClusterCheck clusterCheck : checks) { clusterCheck.check(cluster); assertThatClusters(cluster.getSubclusters()).passRecursively(checks); } } return this; } }