package scotch.compiler.analyzer;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static scotch.compiler.syntax.StubResolver.defaultPlus;
import static scotch.compiler.syntax.definition.DefinitionGraph.cyclicDependency;
import static scotch.util.TestUtil.valueRef;
import static scotch.symbol.Symbol.symbol;
import java.util.List;
import java.util.function.Function;
import org.junit.Before;
import org.junit.Test;
import scotch.compiler.Compiler;
import scotch.compiler.IsolatedCompilerTest;
import scotch.compiler.syntax.definition.DefinitionGraph;
import scotch.compiler.syntax.definition.DependencyCycle;
import scotch.util.TestUtil;
import scotch.symbol.Symbol;
public class AccumulateDependenciesTest extends IsolatedCompilerTest {
@Before
public void setUp() {
super.setUp();
resolver.define(defaultPlus());
}
@Test
public void shouldAccumulateDependencies() {
compile(
"module scotch.test1",
"import scotch.test2",
"import scotch.test3",
"fn1 x = a x",
"b = \\x -> x",
"",
"module scotch.test2",
"import scotch.test1",
"import scotch.test3",
"fn2 y = c b y",
"a = \\y -> y",
"",
"module scotch.test3",
"import scotch.test2",
"c = \\z -> a"
);
shouldNotHaveErrors();
shouldHaveDependencies("scotch.test1.fn1", asList("scotch.test2.a"));
shouldHaveDependencies("scotch.test2.fn2", asList("scotch.test3.c", "scotch.test1.b"));
shouldHaveDependencies("scotch.test3.c", asList("scotch.test2.a"));
}
@Test
public void shouldNotGatherExternalDependencies() {
compile(
"module scotch.test1",
"import scotch.test2",
"import scotch.data.num",
"fn a b = double a + double b",
"",
"module scotch.test2",
"import scotch.data.num",
"double x = x + x"
);
shouldNotHaveErrors();
shouldHaveDependencies("scotch.test1.fn", asList("scotch.test2.double"));
shouldNotHaveDependencies("scotch.test2.double");
}
@Test
public void shouldOrderDependencies() {
compile(
"module scotch.test1",
"import scotch.test2",
"import scotch.test3",
"fn1 x = a x",
"b = \\x -> x",
"",
"module scotch.test2",
"import scotch.test1",
"import scotch.test3",
"fn2 y = c b y",
"a = \\y -> y",
"",
"module scotch.test3",
"import scotch.test2",
"c = \\z -> a"
);
shouldNotHaveErrors();
shouldHaveDependencies(asList(
"scotch.test1.b",
"scotch.test2.a",
"scotch.test1.fn1",
"scotch.test3.c",
"scotch.test2.fn2"
));
}
@Test
public void shouldReportCyclicDependency() {
compile(
"module scotch.test1",
"import scotch.test2",
"import scotch.test3",
"fn1 x = a x",
"b = \\x -> fn2 x",
"",
"module scotch.test2",
"import scotch.test1",
"import scotch.test3",
"fn2 y = c b y",
"a = \\y -> y",
"",
"module scotch.test3",
"import scotch.test2",
"c = \\z -> a"
);
shouldHaveErrors(cyclicDependency(DependencyCycle.builder()
.addNode(symbol("scotch.test1.b"), asList(symbol("scotch.test2.fn2")))
.addNode(symbol("scotch.test2.fn2"), asList(symbol("scotch.test1.b")))
.build()));
}
private void shouldHaveDependencies(String name, List<String> dependencies) {
assertThat(getScope(valueRef(name)).getDependencies(), is(dependencies.stream().map(Symbol::symbol).collect(toSet())));
}
private void shouldHaveDependencies(List<String> dependencies) {
assertThat(graph.getValues(), is(dependencies.stream().map(TestUtil::valueRef).collect(toList())));
}
private void shouldNotHaveDependencies(String name) {
assertThat(getScope(valueRef(name)).getDependencies(), is(empty()));
}
@Override
protected Function<scotch.compiler.Compiler, DefinitionGraph> compile() {
return Compiler::accumulateDependencies;
}
}