// Path: src/test/java/com/github/liblevenshtein/assertion/IteratorAssertions.java
// public static <Type> IteratorAssertions<Type> assertThat(
//     final Iterator<Type> actual) {
//   return new IteratorAssertions<>(actual);
// }


import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.assertThat;
import jdepend.framework.JDepend;
import jdepend.framework.JavaPackage;
import lombok.extern.slf4j.Slf4j;
import static com.github.liblevenshtein.assertion.IteratorAssertions.assertThat;
package com.github.liblevenshtein;








@Slf4j
public class JDependIntegTest {

  private static final String BASE_PACKAGE = "com.github.liblevenshtein";

  @SuppressWarnings("unchecked")
  private static final Set<String> EMPTY_DEPS = (Set<String>) Collections.EMPTY_SET;

  /** Valid, cyclic dependencies. */
  private static final Map<String, Map<String, Set<String>>> VALID_DEPS = buildValidDeps();

  @SuppressWarnings("checkstyle:multiplestringliterals")
  private static Map<String, Map<String, Set<String>>> buildValidDeps() {
    final Map<String, Set<String>> mainDeps = new ImmutableMap.Builder<String, Set<String>>()
      .put("com.github.liblevenshtein.transducer", new ImmutableSet.Builder<String>()
        .add("com.github.liblevenshtein.transducer.factory")
        .add("com.github.liblevenshtein.transducer")
        .build())
      .put("com.github.liblevenshtein.transducer.factory", new ImmutableSet.Builder<String>()
        .add("com.github.liblevenshtein.transducer")
        .add("com.github.liblevenshtein.transducer.factory")
        .build())
      .build();

    // In the tests, pretty much every package is cyclic to every other through
    // their assertions, according to JDepend.  In the future, it may be good to
    // refactor the tests such that their packages are not so coupled.
    final Set<String> assertionDeps = new ImmutableSet.Builder<String>()
      .add("com.github.liblevenshtein.assertion")
      .add("com.github.liblevenshtein.collection.dictionary")
      .add("com.github.liblevenshtein.collection.dictionary.factory")
      .add("com.github.liblevenshtein.distance")
      .add("com.github.liblevenshtein.distance.factory")
      .add("com.github.liblevenshtein.serialization")
      .add("com.github.liblevenshtein.transducer")
      .add("com.github.liblevenshtein.transducer.factory")
      .build();

    final Map<String, Set<String>> testDeps = assertionDeps.stream()
      .reduce(new ImmutableMap.Builder<String, Set<String>>(),
        (builder, dep) -> builder.put(dep, assertionDeps),
        (lhs, rhs) -> lhs).build();

    final Map<String, Set<String>> regrDeps = mainDeps;
    final Map<String, Set<String>> integDeps = mainDeps;

    return new ImmutableMap.Builder<String, Map<String, Set<String>>>()
      .put("main", mainDeps)
      .put("test", testDeps)
      .put("regr", regrDeps)
      .put("integ", integDeps)
      .build();
  }

  @DataProvider(name = "jdependProvider")
  @SuppressWarnings("checkstyle:multiplestringliterals")
  public Object[][] jdependProvider() throws IOException {
    final String[] configs = {"main", "test", "regr", "integ", "task"};
    final Object[][] jdepends = new Object[configs.length][2];
    for (int i = 0; i < configs.length; i += 1) {
      final String config = configs[i];
      try {
        final JDepend jdepend = new JDepend();
        addConfigDir(jdepend, config);
        if (!"main".equals(config) && !"task".equals(config)) {
          addConfigDir(jdepend, "main");
        }
        jdepend.analyze();
        jdepends[i][0] = jdepend;
        jdepends[i][1] = config;
      }
      catch (final IOException exception) {
        log.error("Failed to initialize config [{}]", config, exception);
        throw exception;
      }
    }
    return jdepends;
  }

  private void addConfigDir(final JDepend jdepend, final String config) throws IOException {
    final String projectDir = System.getProperty("user.dir");
    final String classesDir =
      String.format("%s/build/classes/%s", projectDir, config);
    jdepend.addDirectory(classesDir);
  }

  private Set<String> depsFor(final JavaPackage pkg, final String config) {
    if (!VALID_DEPS.containsKey(config)) {
      return EMPTY_DEPS;
    }
    final Map<String, Set<String>> validDeps = VALID_DEPS.get(config);
    if (!validDeps.containsKey(pkg.getName())) {
      return EMPTY_DEPS;
    }
    return validDeps.get(pkg.getName());
  }

  @SuppressWarnings("unchecked")
  @Test(dataProvider = "jdependProvider")
  public void testNoUnexpectedCycles(final JDepend jdepend, final String config) {
    final List<JavaPackage> cycles = new ArrayList<>();
    for (final JavaPackage pkg : (Collection<JavaPackage>) jdepend.getPackages()) {
      if (pkg.getName().startsWith(BASE_PACKAGE)
          && pkg.collectAllCycles(cycles)
          && isDirectCycle(cycles)) {
        final Iterator<JavaPackage> iter = cycles.iterator();
// Path: src/test/java/com/github/liblevenshtein/assertion/IteratorAssertions.java
// public static <Type> IteratorAssertions<Type> assertThat(
//     final Iterator<Type> actual) {
//   return new IteratorAssertions<>(actual);
// }


// Path: src/integ/java/com/github/liblevenshtein/JDependIntegTest.java
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.assertThat;
import jdepend.framework.JDepend;
import jdepend.framework.JavaPackage;
import lombok.extern.slf4j.Slf4j;
import static com.github.liblevenshtein.assertion.IteratorAssertions.assertThat;

package com.github.liblevenshtein;








@Slf4j
public class JDependIntegTest {

  private static final String BASE_PACKAGE = "com.github.liblevenshtein";

  @SuppressWarnings("unchecked")
  private static final Set<String> EMPTY_DEPS = (Set<String>) Collections.EMPTY_SET;

  /** Valid, cyclic dependencies. */
  private static final Map<String, Map<String, Set<String>>> VALID_DEPS = buildValidDeps();

  @SuppressWarnings("checkstyle:multiplestringliterals")
  private static Map<String, Map<String, Set<String>>> buildValidDeps() {
    final Map<String, Set<String>> mainDeps = new ImmutableMap.Builder<String, Set<String>>()
      .put("com.github.liblevenshtein.transducer", new ImmutableSet.Builder<String>()
        .add("com.github.liblevenshtein.transducer.factory")
        .add("com.github.liblevenshtein.transducer")
        .build())
      .put("com.github.liblevenshtein.transducer.factory", new ImmutableSet.Builder<String>()
        .add("com.github.liblevenshtein.transducer")
        .add("com.github.liblevenshtein.transducer.factory")
        .build())
      .build();

    // In the tests, pretty much every package is cyclic to every other through
    // their assertions, according to JDepend.  In the future, it may be good to
    // refactor the tests such that their packages are not so coupled.
    final Set<String> assertionDeps = new ImmutableSet.Builder<String>()
      .add("com.github.liblevenshtein.assertion")
      .add("com.github.liblevenshtein.collection.dictionary")
      .add("com.github.liblevenshtein.collection.dictionary.factory")
      .add("com.github.liblevenshtein.distance")
      .add("com.github.liblevenshtein.distance.factory")
      .add("com.github.liblevenshtein.serialization")
      .add("com.github.liblevenshtein.transducer")
      .add("com.github.liblevenshtein.transducer.factory")
      .build();

    final Map<String, Set<String>> testDeps = assertionDeps.stream()
      .reduce(new ImmutableMap.Builder<String, Set<String>>(),
        (builder, dep) -> builder.put(dep, assertionDeps),
        (lhs, rhs) -> lhs).build();

    final Map<String, Set<String>> regrDeps = mainDeps;
    final Map<String, Set<String>> integDeps = mainDeps;

    return new ImmutableMap.Builder<String, Map<String, Set<String>>>()
      .put("main", mainDeps)
      .put("test", testDeps)
      .put("regr", regrDeps)
      .put("integ", integDeps)
      .build();
  }

  @DataProvider(name = "jdependProvider")
  @SuppressWarnings("checkstyle:multiplestringliterals")
  public Object[][] jdependProvider() throws IOException {
    final String[] configs = {"main", "test", "regr", "integ", "task"};
    final Object[][] jdepends = new Object[configs.length][2];
    for (int i = 0; i < configs.length; i += 1) {
      final String config = configs[i];
      try {
        final JDepend jdepend = new JDepend();
        addConfigDir(jdepend, config);
        if (!"main".equals(config) && !"task".equals(config)) {
          addConfigDir(jdepend, "main");
        }
        jdepend.analyze();
        jdepends[i][0] = jdepend;
        jdepends[i][1] = config;
      }
      catch (final IOException exception) {
        log.error("Failed to initialize config [{}]", config, exception);
        throw exception;
      }
    }
    return jdepends;
  }

  private void addConfigDir(final JDepend jdepend, final String config) throws IOException {
    final String projectDir = System.getProperty("user.dir");
    final String classesDir =
      String.format("%s/build/classes/%s", projectDir, config);
    jdepend.addDirectory(classesDir);
  }

  private Set<String> depsFor(final JavaPackage pkg, final String config) {
    if (!VALID_DEPS.containsKey(config)) {
      return EMPTY_DEPS;
    }
    final Map<String, Set<String>> validDeps = VALID_DEPS.get(config);
    if (!validDeps.containsKey(pkg.getName())) {
      return EMPTY_DEPS;
    }
    return validDeps.get(pkg.getName());
  }

  @SuppressWarnings("unchecked")
  @Test(dataProvider = "jdependProvider")
  public void testNoUnexpectedCycles(final JDepend jdepend, final String config) {
    final List<JavaPackage> cycles = new ArrayList<>();
    for (final JavaPackage pkg : (Collection<JavaPackage>) jdepend.getPackages()) {
      if (pkg.getName().startsWith(BASE_PACKAGE)
          && pkg.collectAllCycles(cycles)
          && isDirectCycle(cycles)) {
        final Iterator<JavaPackage> iter = cycles.iterator();
        assertThat(iter).hasNext();