blob: dd946507a5eb8ee5bb1e48d22152f505afed3770 [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed 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 com.google.turbine.deps;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.lower.IntegrationTestSupport;
import com.google.turbine.lower.Lower;
import com.google.turbine.lower.Lower.Lowered;
import com.google.turbine.parse.Parser;
import com.google.turbine.proto.DepsProto;
import com.google.turbine.tree.Tree.CompUnit;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class DependenciesTest {
static final ImmutableSet<Path> BOOTCLASSPATH =
ImmutableSet.of(Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar"));
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
class LibraryBuilder {
final Map<String, String> sources = new LinkedHashMap<>();
private ImmutableList<Path> classpath = ImmutableList.of();
LibraryBuilder addSourceLines(String path, String... lines) {
sources.put(path, Joiner.on('\n').join(lines));
return this;
}
LibraryBuilder setClasspath(Path... classpath) {
this.classpath = ImmutableList.copyOf(classpath);
return this;
}
Path compileToJar(String path) throws Exception {
Path lib = temporaryFolder.newFile(path).toPath();
Map<String, byte[]> classes =
IntegrationTestSupport.runJavac(sources, classpath, BOOTCLASSPATH);
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
for (Map.Entry<String, byte[]> entry : classes.entrySet()) {
jos.putNextEntry(new JarEntry(entry.getKey() + ".class"));
jos.write(entry.getValue());
}
}
return lib;
}
}
static class DepsBuilder {
List<Path> classpath;
List<CompUnit> units = new ArrayList<>();
DepsBuilder setClasspath(Path... classpath) {
this.classpath = ImmutableList.copyOf(classpath);
return this;
}
DepsBuilder addSourceLines(String path, String... lines) {
units.add(Parser.parse(new SourceFile(path, Joiner.on('\n').join(lines))));
return this;
}
DepsProto.Dependencies run() throws IOException {
BindingResult bound = Binder.bind(units, classpath, BOOTCLASSPATH);
Lowered lowered = Lower.lowerAll(bound.units(), bound.classPathEnv());
return Dependencies.collectDeps(
Optional.of("//test"),
ImmutableSet.copyOf(Iterables.transform(BOOTCLASSPATH, Path::toString)),
bound,
lowered);
}
}
private Map<Path, DepsProto.Dependency.Kind> depsMap(DepsProto.Dependencies deps) {
return StreamSupport.stream(deps.getDependencyList().spliterator(), false)
.collect(Collectors.toMap(d -> Paths.get(d.getPath()), DepsProto.Dependency::getKind));
}
@Test
public void simple() throws Exception {
Path liba =
new LibraryBuilder().addSourceLines("A.java", "class A {}").compileToJar("liba.jar");
DepsProto.Dependencies deps =
new DepsBuilder()
.setClasspath(liba)
.addSourceLines("Test.java", "class Test extends A {}")
.run();
assertThat(depsMap(deps)).isEqualTo(ImmutableMap.of(liba, DepsProto.Dependency.Kind.EXPLICIT));
}
@Test
public void excluded() throws Exception {
Path liba =
new LibraryBuilder()
.addSourceLines(
"A.java", //
"class A {}")
.compileToJar("liba.jar");
Path libb =
new LibraryBuilder()
.setClasspath(liba)
.addSourceLines(
"B.java", //
"class B {",
" public static final A a = new A();",
"}")
.compileToJar("libb.jar");
DepsProto.Dependencies deps =
new DepsBuilder()
.setClasspath(liba, libb)
.addSourceLines("Test.java", "class Test extends B {}")
.run();
assertThat(depsMap(deps)).isEqualTo(ImmutableMap.of(libb, DepsProto.Dependency.Kind.EXPLICIT));
}
@Test
public void transitive() throws Exception {
Path liba =
new LibraryBuilder()
.addSourceLines(
"A.java", //
"class A {",
" public static final class Y {}",
"}")
.compileToJar("liba.jar");
Path libb =
new LibraryBuilder()
.setClasspath(liba)
.addSourceLines("B.java", "class B extends A {}")
.compileToJar("libb.jar");
DepsProto.Dependencies deps =
new DepsBuilder()
.setClasspath(liba, libb)
.addSourceLines(
"Test.java", //
"class Test extends B {",
" public static class X extends Y {}",
"}")
.run();
assertThat(depsMap(deps))
.isEqualTo(
ImmutableMap.of(
libb, DepsProto.Dependency.Kind.EXPLICIT,
liba, DepsProto.Dependency.Kind.EXPLICIT));
}
@Test
public void closure() throws Exception {
Path libi =
new LibraryBuilder()
.addSourceLines(
"i/I.java",
"package i;", //
"public interface I {}")
.compileToJar("libi.jar");
Path liba =
new LibraryBuilder()
.setClasspath(libi)
.addSourceLines(
"a/A.java", //
"package a;",
"import i.I;",
"public class A implements I {}")
.compileToJar("liba.jar");
Path libb =
new LibraryBuilder()
.setClasspath(liba, libi)
.addSourceLines(
"b/B.java", //
"package b;",
"import a.A;",
"public class B extends A {}")
.compileToJar("libb.jar");
{
DepsProto.Dependencies deps =
new DepsBuilder()
.setClasspath(liba, libb, libi)
.addSourceLines(
"Test.java", //
"import b.B;",
"class Test extends B {}")
.run();
assertThat(depsMap(deps))
.isEqualTo(
ImmutableMap.of(
libi, DepsProto.Dependency.Kind.EXPLICIT,
libb, DepsProto.Dependency.Kind.EXPLICIT,
liba, DepsProto.Dependency.Kind.EXPLICIT));
}
{
// partial classpath
DepsProto.Dependencies deps =
new DepsBuilder()
.setClasspath(liba, libb)
.addSourceLines(
"Test.java", //
"import b.B;",
"class Test extends B {}")
.run();
assertThat(depsMap(deps))
.isEqualTo(
ImmutableMap.of(
libb, DepsProto.Dependency.Kind.EXPLICIT,
liba, DepsProto.Dependency.Kind.EXPLICIT));
}
}
@Test
public void unreducedClasspathTest() throws IOException {
ImmutableList<String> classpath =
ImmutableList.of(
"a.jar", "b.jar", "c.jar", "d.jar", "e.jar", "f.jar", "g.jar", "h.jar", "i.jar",
"j.jar");
ImmutableMap<String, String> directJarsToTargets = ImmutableMap.of();
ImmutableList<String> depsArtifacts = ImmutableList.of();
assertThat(Dependencies.reduceClasspath(classpath, directJarsToTargets, depsArtifacts))
.isEqualTo(classpath);
}
@Test
public void reducedClasspathTest() throws IOException {
Path cdeps = temporaryFolder.newFile("c.jdeps").toPath();
Path ddeps = temporaryFolder.newFile("d.jdeps").toPath();
Path gdeps = temporaryFolder.newFile("g.jdeps").toPath();
ImmutableList<String> classpath =
ImmutableList.of(
"a.jar", "b.jar", "c.jar", "d.jar", "e.jar", "f.jar", "g.jar", "h.jar", "i.jar",
"j.jar");
ImmutableMap<String, String> directJarsToTargets =
ImmutableMap.of(
"c.jar", "//a",
"d.jar", "//d",
"g.jar", "//e");
ImmutableList<String> depsArtifacts =
ImmutableList.of(cdeps.toString(), ddeps.toString(), gdeps.toString());
writeDeps(
cdeps,
ImmutableMap.of(
"b.jar", DepsProto.Dependency.Kind.EXPLICIT,
"e.jar", DepsProto.Dependency.Kind.EXPLICIT));
writeDeps(
ddeps,
ImmutableMap.of(
"f.jar", DepsProto.Dependency.Kind.UNUSED,
"j.jar", DepsProto.Dependency.Kind.UNUSED));
writeDeps(gdeps, ImmutableMap.of("i.jar", DepsProto.Dependency.Kind.IMPLICIT));
assertThat(Dependencies.reduceClasspath(classpath, directJarsToTargets, depsArtifacts))
.containsExactly("b.jar", "c.jar", "d.jar", "e.jar", "g.jar", "i.jar")
.inOrder();
}
@Test
public void packageInfo() throws Exception {
Path libpackageInfo =
new LibraryBuilder()
.addSourceLines(
"p/Anno.java",
"package p;",
"import java.lang.annotation.Retention;",
"import static java.lang.annotation.RetentionPolicy.RUNTIME;",
"@Retention(RUNTIME)",
"@interface Anno {}")
.addSourceLines(
"p/package-info.java", //
"@Anno",
"package p;")
.compileToJar("libpackage-info.jar");
Path libp =
new LibraryBuilder()
.setClasspath(libpackageInfo)
.addSourceLines(
"p/P.java", //
"package p;",
"public class P {}")
.compileToJar("libp.jar");
{
DepsProto.Dependencies deps =
new DepsBuilder()
.setClasspath(libp, libpackageInfo)
.addSourceLines(
"Test.java", //
"import p.P;",
"class Test {",
" P p;",
"}")
.run();
assertThat(depsMap(deps))
.containsExactly(
libpackageInfo,
DepsProto.Dependency.Kind.EXPLICIT,
libp,
DepsProto.Dependency.Kind.EXPLICIT);
}
}
void writeDeps(Path path, ImmutableMap<String, DepsProto.Dependency.Kind> deps)
throws IOException {
DepsProto.Dependencies.Builder builder =
DepsProto.Dependencies.newBuilder().setSuccess(true).setRuleLabel("//test");
for (Map.Entry<String, DepsProto.Dependency.Kind> e : deps.entrySet()) {
builder.addDependency(
DepsProto.Dependency.newBuilder().setPath(e.getKey()).setKind(e.getValue()));
}
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {
builder.build().writeTo(os);
}
}
}