<commit_before>/*
 * Carrot2 project.
 *
 * Copyright (C) 2002-2011, <NAME>, <NAME>.
 * 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 com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.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());

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < Math.max(l1.size(), l2.size()); i++)
        {
            sb.append(i < l1.size() ? Strings.padEnd(l1.get(i), maxL1Width, ' ') : "");
            sb.append(" | ");
            sb.append(i < l2.size() ? l2.get(i) : "");
            sb.append("\n");
        }

        sb.append("\n");
        Set<String> l1s = Sets.newTreeSet(l1);
        Set<String> l2s = Sets.newTreeSet(l2);

        if (l1s.equals(l2s))
        {
            sb.append("PROBLEM: Same sets different order or hierarchy.\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");            
        }

        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)<commit_msg>Better side-by-side view of labels.<commit_after>/*
 * Carrot2 project.
 *
 * Copyright (C) 2002-2011, <NAME>, <NAME>.
 * 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 com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.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");
        }
        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)
{'fraction_non_alphanumeric': 0.11061218380481964, 'fraction_numerical': 0.007932944170034427, 'mean_word_length': 2.4458483754512637, 'pattern_counts': {'":': 2, '<': 59, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '263151', 'n_tokens_mistral': 3807, 'n_tokens_neox': 3688, 'n_words': 922}