xref: /aosp_15_r20/external/turbine/javatests/com/google/turbine/deps/DependenciesTest.java (revision f7d94438c8bcdfdbf0d5a2a5e40120d0696e7088)
1 /*
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.deps;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import com.google.common.base.Joiner;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableMap;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.turbine.binder.Binder;
26 import com.google.turbine.binder.Binder.BindingResult;
27 import com.google.turbine.binder.ClassPathBinder;
28 import com.google.turbine.diag.SourceFile;
29 import com.google.turbine.lower.IntegrationTestSupport;
30 import com.google.turbine.lower.Lower;
31 import com.google.turbine.lower.Lower.Lowered;
32 import com.google.turbine.parse.Parser;
33 import com.google.turbine.proto.DepsProto;
34 import com.google.turbine.testing.TestClassPaths;
35 import com.google.turbine.tree.Tree.CompUnit;
36 import java.io.BufferedOutputStream;
37 import java.io.IOException;
38 import java.io.OutputStream;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.nio.file.Paths;
42 import java.util.LinkedHashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Optional;
46 import java.util.jar.JarEntry;
47 import java.util.jar.JarOutputStream;
48 import java.util.stream.Collectors;
49 import org.junit.Rule;
50 import org.junit.Test;
51 import org.junit.rules.TemporaryFolder;
52 import org.junit.runner.RunWith;
53 import org.junit.runners.JUnit4;
54 
55 @RunWith(JUnit4.class)
56 public class DependenciesTest {
57 
58   @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
59 
60   class LibraryBuilder {
61     final Map<String, String> sources = new LinkedHashMap<>();
62     private ImmutableList<Path> classpath = ImmutableList.of();
63 
addSourceLines(String path, String... lines)64     LibraryBuilder addSourceLines(String path, String... lines) {
65       sources.put(path, Joiner.on('\n').join(lines));
66       return this;
67     }
68 
setClasspath(Path... classpath)69     LibraryBuilder setClasspath(Path... classpath) {
70       this.classpath = ImmutableList.copyOf(classpath);
71       return this;
72     }
73 
compileToJar(String path)74     Path compileToJar(String path) throws Exception {
75       Path lib = temporaryFolder.newFile(path).toPath();
76       Map<String, byte[]> classes = IntegrationTestSupport.runJavac(sources, classpath);
77       try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
78         for (Map.Entry<String, byte[]> entry : classes.entrySet()) {
79           jos.putNextEntry(new JarEntry(entry.getKey() + ".class"));
80           jos.write(entry.getValue());
81         }
82       }
83       return lib;
84     }
85   }
86 
87   static class DepsBuilder {
88     List<Path> classpath;
89     ImmutableList.Builder<CompUnit> units = ImmutableList.builder();
90 
setClasspath(Path... classpath)91     DepsBuilder setClasspath(Path... classpath) {
92       this.classpath = ImmutableList.copyOf(classpath);
93       return this;
94     }
95 
addSourceLines(String path, String... lines)96     DepsBuilder addSourceLines(String path, String... lines) {
97       units.add(Parser.parse(new SourceFile(path, Joiner.on('\n').join(lines))));
98       return this;
99     }
100 
run()101     DepsProto.Dependencies run() throws IOException {
102       BindingResult bound =
103           Binder.bind(
104               units.build(),
105               ClassPathBinder.bindClasspath(classpath),
106               TestClassPaths.TURBINE_BOOTCLASSPATH,
107               /* moduleVersion= */ Optional.empty());
108 
109       Lowered lowered =
110           Lower.lowerAll(
111               Lower.LowerOptions.createDefault(),
112               bound.units(),
113               bound.modules(),
114               bound.classPathEnv());
115 
116       return Dependencies.collectDeps(
117           Optional.of("//test"), TestClassPaths.TURBINE_BOOTCLASSPATH, bound, lowered);
118     }
119   }
120 
depsMap(DepsProto.Dependencies deps)121   private Map<Path, DepsProto.Dependency.Kind> depsMap(DepsProto.Dependencies deps) {
122     return deps.getDependencyList().stream()
123         .collect(Collectors.toMap(d -> Paths.get(d.getPath()), DepsProto.Dependency::getKind));
124   }
125 
126   @Test
simple()127   public void simple() throws Exception {
128     Path liba =
129         new LibraryBuilder().addSourceLines("A.java", "class A {}").compileToJar("liba.jar");
130     DepsProto.Dependencies deps =
131         new DepsBuilder()
132             .setClasspath(liba)
133             .addSourceLines("Test.java", "class Test extends A {}")
134             .run();
135 
136     assertThat(depsMap(deps)).containsExactly(liba, DepsProto.Dependency.Kind.EXPLICIT);
137   }
138 
139   @Test
excluded()140   public void excluded() throws Exception {
141     Path liba =
142         new LibraryBuilder()
143             .addSourceLines(
144                 "A.java", //
145                 "class A {}")
146             .compileToJar("liba.jar");
147     Path libb =
148         new LibraryBuilder()
149             .setClasspath(liba)
150             .addSourceLines(
151                 "B.java", //
152                 "class B {",
153                 "  public static final A a = new A();",
154                 "}")
155             .compileToJar("libb.jar");
156     DepsProto.Dependencies deps =
157         new DepsBuilder()
158             .setClasspath(liba, libb)
159             .addSourceLines("Test.java", "class Test extends B {}")
160             .run();
161 
162     assertThat(depsMap(deps)).containsExactly(libb, DepsProto.Dependency.Kind.EXPLICIT);
163   }
164 
165   @Test
transitive()166   public void transitive() throws Exception {
167     Path liba =
168         new LibraryBuilder()
169             .addSourceLines(
170                 "A.java", //
171                 "class A {",
172                 "  public static final class Y {}",
173                 "}")
174             .compileToJar("liba.jar");
175     Path libb =
176         new LibraryBuilder()
177             .setClasspath(liba)
178             .addSourceLines("B.java", "class B extends A {}")
179             .compileToJar("libb.jar");
180     DepsProto.Dependencies deps =
181         new DepsBuilder()
182             .setClasspath(liba, libb)
183             .addSourceLines(
184                 "Test.java", //
185                 "class Test extends B {",
186                 "  public static class X extends Y {}",
187                 "}")
188             .run();
189     assertThat(depsMap(deps))
190         .containsExactly(
191             libb, DepsProto.Dependency.Kind.EXPLICIT, liba, DepsProto.Dependency.Kind.EXPLICIT);
192   }
193 
194   @Test
closure()195   public void closure() throws Exception {
196     Path libi =
197         new LibraryBuilder()
198             .addSourceLines(
199                 "i/I.java",
200                 "package i;", //
201                 "public interface I {}")
202             .compileToJar("libi.jar");
203     Path liba =
204         new LibraryBuilder()
205             .setClasspath(libi)
206             .addSourceLines(
207                 "a/A.java", //
208                 "package a;",
209                 "import i.I;",
210                 "public class A implements I {}")
211             .compileToJar("liba.jar");
212     Path libb =
213         new LibraryBuilder()
214             .setClasspath(liba, libi)
215             .addSourceLines(
216                 "b/B.java", //
217                 "package b;",
218                 "import a.A;",
219                 "public class B extends A {}")
220             .compileToJar("libb.jar");
221     {
222       DepsProto.Dependencies deps =
223           new DepsBuilder()
224               .setClasspath(liba, libb, libi)
225               .addSourceLines(
226                   "Test.java", //
227                   "import b.B;",
228                   "class Test extends B {}")
229               .run();
230       assertThat(depsMap(deps))
231           .containsExactly(
232               libi,
233               DepsProto.Dependency.Kind.EXPLICIT,
234               libb,
235               DepsProto.Dependency.Kind.EXPLICIT,
236               liba,
237               DepsProto.Dependency.Kind.EXPLICIT);
238     }
239     {
240       // partial classpath
241       DepsProto.Dependencies deps =
242           new DepsBuilder()
243               .setClasspath(liba, libb)
244               .addSourceLines(
245                   "Test.java", //
246                   "import b.B;",
247                   "class Test extends B {}")
248               .run();
249       assertThat(depsMap(deps))
250           .containsExactly(
251               libb, DepsProto.Dependency.Kind.EXPLICIT, liba, DepsProto.Dependency.Kind.EXPLICIT);
252     }
253   }
254 
255   @Test
unreducedClasspathTest()256   public void unreducedClasspathTest() throws IOException {
257     ImmutableList<String> classpath =
258         ImmutableList.of(
259             "a.jar", "b.jar", "c.jar", "d.jar", "e.jar", "f.jar", "g.jar", "h.jar", "i.jar",
260             "j.jar");
261     ImmutableSet<String> directJars = ImmutableSet.of();
262     ImmutableList<String> depsArtifacts = ImmutableList.of();
263     assertThat(Dependencies.reduceClasspath(classpath, directJars, depsArtifacts))
264         .containsExactlyElementsIn(classpath);
265   }
266 
267   @Test
reducedClasspathTest()268   public void reducedClasspathTest() throws IOException {
269     Path cdeps = temporaryFolder.newFile("c.jdeps").toPath();
270     Path ddeps = temporaryFolder.newFile("d.jdeps").toPath();
271     Path gdeps = temporaryFolder.newFile("g.jdeps").toPath();
272     ImmutableList<String> classpath =
273         ImmutableList.of(
274             "a.jar", "b.jar", "c.jar", "d.jar", "e.jar", "f.jar", "g.jar", "h.jar", "i.jar",
275             "j.jar");
276     ImmutableSet<String> directJars = ImmutableSet.of("c.jar", "d.jar", "g.jar");
277     ImmutableList<String> depsArtifacts =
278         ImmutableList.of(cdeps.toString(), ddeps.toString(), gdeps.toString());
279     writeDeps(
280         cdeps,
281         ImmutableMap.of(
282             "b.jar", DepsProto.Dependency.Kind.EXPLICIT,
283             "e.jar", DepsProto.Dependency.Kind.EXPLICIT));
284     writeDeps(
285         ddeps,
286         ImmutableMap.of(
287             "f.jar", DepsProto.Dependency.Kind.UNUSED,
288             "j.jar", DepsProto.Dependency.Kind.UNUSED));
289     writeDeps(gdeps, ImmutableMap.of("i.jar", DepsProto.Dependency.Kind.IMPLICIT));
290     assertThat(Dependencies.reduceClasspath(classpath, directJars, depsArtifacts))
291         .containsExactly("b.jar", "c.jar", "d.jar", "e.jar", "g.jar", "i.jar")
292         .inOrder();
293   }
294 
295   @Test
packageInfo()296   public void packageInfo() throws Exception {
297     Path libpackageInfo =
298         new LibraryBuilder()
299             .addSourceLines(
300                 "p/Anno.java",
301                 "package p;",
302                 "import java.lang.annotation.Retention;",
303                 "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
304                 "@Retention(RUNTIME)",
305                 "@interface Anno {}")
306             .addSourceLines(
307                 "p/package-info.java", //
308                 "@Anno",
309                 "package p;")
310             .compileToJar("libpackage-info.jar");
311     Path libp =
312         new LibraryBuilder()
313             .setClasspath(libpackageInfo)
314             .addSourceLines(
315                 "p/P.java", //
316                 "package p;",
317                 "public class P {}")
318             .compileToJar("libp.jar");
319     {
320       DepsProto.Dependencies deps =
321           new DepsBuilder()
322               .setClasspath(libp, libpackageInfo)
323               .addSourceLines(
324                   "Test.java", //
325                   "import p.P;",
326                   "class Test {",
327                   "  P p;",
328                   "}")
329               .run();
330       assertThat(depsMap(deps))
331           .containsExactly(
332               libpackageInfo,
333               DepsProto.Dependency.Kind.EXPLICIT,
334               libp,
335               DepsProto.Dependency.Kind.EXPLICIT);
336     }
337   }
338 
339   @Test
annotations_recursive()340   public void annotations_recursive() throws Exception {
341     Path libA = libA();
342     Path libB = libB();
343 
344     DepsProto.Dependencies deps =
345         new DepsBuilder()
346             .setClasspath(libA, libB)
347             .addSourceLines(
348                 "Test.java", //
349                 "import a.A;",
350                 "import b.B;",
351                 "@A(B.class)",
352                 "class Test {",
353                 "}")
354             .run();
355     assertThat(depsMap(deps))
356         .containsExactly(
357             libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
358   }
359 
360   @Test
annotations_field()361   public void annotations_field() throws Exception {
362     Path libA = libA();
363     Path libB = libB();
364     DepsProto.Dependencies deps =
365         new DepsBuilder()
366             .setClasspath(libA, libB)
367             .addSourceLines(
368                 "Test.java", //
369                 "import a.A;",
370                 "import b.B;",
371                 "class Test {",
372                 "  @A(B.class)",
373                 "  int x;",
374                 "}")
375             .run();
376     assertThat(depsMap(deps))
377         .containsExactly(
378             libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
379   }
380 
381   @Test
annotations_method()382   public void annotations_method() throws Exception {
383     Path libA = libA();
384     Path libB = libB();
385     DepsProto.Dependencies deps =
386         new DepsBuilder()
387             .setClasspath(libA, libB)
388             .addSourceLines(
389                 "Test.java", //
390                 "import a.A;",
391                 "import b.B;",
392                 "class Test {",
393                 "  @A(B.class)",
394                 "  void f() {}",
395                 "}")
396             .run();
397     assertThat(depsMap(deps))
398         .containsExactly(
399             libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
400   }
401 
libB()402   private Path libB() throws Exception {
403     return new LibraryBuilder()
404         .addSourceLines(
405             "b/B.java",
406             "package b;",
407             "import java.lang.annotation.Retention;",
408             "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
409             "@Retention(RUNTIME)",
410             "public @interface B {",
411             "}")
412         .compileToJar("libb.jar");
413   }
414 
libA()415   private Path libA() throws Exception {
416     return new LibraryBuilder()
417         .addSourceLines(
418             "a/A.java",
419             "package a;",
420             "import java.lang.annotation.Retention;",
421             "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
422             "@Retention(RUNTIME)",
423             "public @interface A {",
424             "  Class<?> value() default Object.class;",
425             "}")
426         .compileToJar("liba.jar");
427   }
428 
writeDeps(Path path, ImmutableMap<String, DepsProto.Dependency.Kind> deps)429   void writeDeps(Path path, ImmutableMap<String, DepsProto.Dependency.Kind> deps)
430       throws IOException {
431     DepsProto.Dependencies.Builder builder =
432         DepsProto.Dependencies.newBuilder().setSuccess(true).setRuleLabel("//test");
433     for (Map.Entry<String, DepsProto.Dependency.Kind> e : deps.entrySet()) {
434       builder.addDependency(
435           DepsProto.Dependency.newBuilder().setPath(e.getKey()).setKind(e.getValue()));
436     }
437     try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {
438       builder.build().writeTo(os);
439     }
440   }
441 }
442