• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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.main;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
21 import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
22 import static java.nio.charset.StandardCharsets.UTF_8;
23 import static java.util.Objects.requireNonNull;
24 import static org.junit.Assert.assertThrows;
25 
26 import com.google.common.base.Joiner;
27 import com.google.common.collect.ImmutableList;
28 import com.google.protobuf.ExtensionRegistry;
29 import com.google.turbine.diag.TurbineError;
30 import com.google.turbine.lower.IntegrationTestSupport;
31 import com.google.turbine.lower.IntegrationTestSupport.TestInput;
32 import com.google.turbine.main.Main.Result;
33 import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
34 import com.google.turbine.proto.DepsProto;
35 import com.google.turbine.proto.DepsProto.Dependency.Kind;
36 import java.io.BufferedInputStream;
37 import java.io.BufferedOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.nio.file.Files;
42 import java.nio.file.Path;
43 import java.util.Map;
44 import java.util.jar.JarEntry;
45 import java.util.jar.JarOutputStream;
46 import org.junit.Before;
47 import org.junit.Rule;
48 import org.junit.Test;
49 import org.junit.rules.TemporaryFolder;
50 import org.junit.runner.RunWith;
51 import org.junit.runners.JUnit4;
52 
53 @RunWith(JUnit4.class)
54 public class ReducedClasspathTest {
55 
56   @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
57 
58   private Path liba;
59   private Path libb;
60   private Path libc;
61   private Path libcJdeps;
62 
63   @Before
setup()64   public void setup() throws Exception {
65     Map<String, byte[]> compiled =
66         IntegrationTestSupport.runJavac(
67             TestInput.parse(
68                     String.join(
69                         "\n",
70                         ImmutableList.of(
71                             "=== a/A.java ===",
72                             "package a;",
73                             "public class A {",
74                             "  public static class I {}",
75                             "}",
76                             "=== b/B.java ===",
77                             "package b;",
78                             "import a.A;",
79                             "public class B extends A {}",
80                             "=== c/C.java ===",
81                             "package c;",
82                             "import b.B;",
83                             "public class C extends B {}")))
84                 .sources,
85             /* classpath= */ ImmutableList.of());
86 
87     liba = createLibrary(compiled, "liba.jar", "a/A", "a/A$I");
88     libb = createLibrary(compiled, "libb.jar", "b/B");
89     libc = createLibrary(compiled, "libc.jar", "c/C");
90 
91     libcJdeps = temporaryFolder.newFile("libc.jdeps").toPath();
92     try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(libcJdeps))) {
93       DepsProto.Dependencies.newBuilder()
94           .addDependency(
95               DepsProto.Dependency.newBuilder()
96                   .setKind(Kind.EXPLICIT)
97                   .setPath(libb.toString())
98                   .build())
99           .build()
100           .writeTo(os);
101     }
102   }
103 
createLibrary(Map<String, byte[]> compiled, String jarPath, String... classNames)104   private Path createLibrary(Map<String, byte[]> compiled, String jarPath, String... classNames)
105       throws IOException {
106     Path lib = temporaryFolder.newFile(jarPath).toPath();
107     try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
108       for (String className : classNames) {
109         jos.putNextEntry(new JarEntry(className + ".class"));
110         jos.write(requireNonNull(compiled.get(className), className));
111       }
112     }
113     return lib;
114   }
115 
116   @Test
succeedsWithoutFallingBack()117   public void succeedsWithoutFallingBack() throws Exception {
118     Path src = temporaryFolder.newFile("Test.java").toPath();
119     Files.write(
120         src,
121         ImmutableList.of(
122             "import c.C;", //
123             "class Test extends C {",
124             "}"),
125         UTF_8);
126 
127     Path output = temporaryFolder.newFile("output.jar").toPath();
128 
129     Result result =
130         Main.compile(
131             optionsWithBootclasspath()
132                 .setOutput(output.toString())
133                 .setSources(ImmutableList.of(src.toString()))
134                 .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
135                 .setClassPath(
136                     ImmutableList.of(
137                         // ensure that the compilation succeeds without falling back by adding
138                         // a jar to the transitive classpath that doesn't exist, which would cause
139                         // the compilation to fail if it fell back
140                         temporaryFolder.newFile("no.such.jar").toString(),
141                         liba.toString(),
142                         libb.toString(),
143                         libc.toString()))
144                 .setDirectJars(ImmutableList.of(libc.toString()))
145                 .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
146                 .build());
147     assertThat(result.transitiveClasspathFallback()).isFalse();
148   }
149 
150   @Test
succeedsAfterFallingBack()151   public void succeedsAfterFallingBack() throws Exception {
152     Path src = temporaryFolder.newFile("Test.java").toPath();
153     Files.write(
154         src,
155         ImmutableList.of(
156             "import c.C;", //
157             "class Test extends C {",
158             "  I i;",
159             "}"),
160         UTF_8);
161 
162     Path output = temporaryFolder.newFile("output.jar").toPath();
163 
164     Result result =
165         Main.compile(
166             optionsWithBootclasspath()
167                 .setOutput(output.toString())
168                 .setSources(ImmutableList.of(src.toString()))
169                 .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
170                 .setClassPath(ImmutableList.of(liba.toString(), libb.toString(), libc.toString()))
171                 .setDirectJars(ImmutableList.of(libc.toString()))
172                 .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
173                 .build());
174     assertThat(result.transitiveClasspathFallback()).isTrue();
175     assertThat(result.reducedClasspathLength()).isEqualTo(2);
176     assertThat(result.transitiveClasspathLength()).isEqualTo(3);
177   }
178 
179   @Test
bazelFallback()180   public void bazelFallback() throws Exception {
181     Path src = temporaryFolder.newFile("Test.java").toPath();
182     Files.write(
183         src,
184         ImmutableList.of(
185             "import c.C;", //
186             "class Test extends C {",
187             "  I i;",
188             "}"),
189         UTF_8);
190 
191     Path output = temporaryFolder.newFile("output.jar").toPath();
192     Path jdeps = temporaryFolder.newFile("output.jdeps").toPath();
193 
194     Result result =
195         Main.compile(
196             optionsWithBootclasspath()
197                 .setOutput(output.toString())
198                 .setTargetLabel("//java/com/google/foo")
199                 .setOutputDeps(jdeps.toString())
200                 .setSources(ImmutableList.of(src.toString()))
201                 .setReducedClasspathMode(ReducedClasspathMode.BAZEL_REDUCED)
202                 .setClassPath(ImmutableList.of(libc.toString()))
203                 .setReducedClasspathLength(1)
204                 .setFullClasspathLength(3)
205                 .build());
206     assertThat(result.transitiveClasspathFallback()).isTrue();
207     assertThat(result.reducedClasspathLength()).isEqualTo(1);
208     assertThat(result.transitiveClasspathLength()).isEqualTo(3);
209     DepsProto.Dependencies.Builder deps = DepsProto.Dependencies.newBuilder();
210     try (InputStream is = new BufferedInputStream(Files.newInputStream(jdeps))) {
211       deps.mergeFrom(is, ExtensionRegistry.getEmptyRegistry());
212     }
213     assertThat(deps.build())
214         .isEqualTo(
215             DepsProto.Dependencies.newBuilder()
216                 .setRequiresReducedClasspathFallback(true)
217                 .setRuleLabel("//java/com/google/foo")
218                 .build());
219   }
220 
221   @Test
noFallbackWithoutDirectJarsAndJdeps()222   public void noFallbackWithoutDirectJarsAndJdeps() throws Exception {
223     Path src = temporaryFolder.newFile("Test.java").toPath();
224     Files.write(
225         src,
226         ImmutableList.of(
227             "import c.C;", //
228             "class Test extends C {",
229             "  I i;",
230             "}"),
231         UTF_8);
232 
233     Path output = temporaryFolder.newFile("output.jar").toPath();
234 
235     TurbineError e =
236         assertThrows(
237             TurbineError.class,
238             () ->
239                 Main.compile(
240                     optionsWithBootclasspath()
241                         .setOutput(output.toString())
242                         .setSources(ImmutableList.of(src.toString()))
243                         .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
244                         .setClassPath(ImmutableList.of(libc.toString()))
245                         .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
246                         .build()));
247     assertThat(e).hasMessageThat().contains("could not resolve I");
248   }
249 
lines(String... lines)250   static String lines(String... lines) {
251     return Joiner.on(System.lineSeparator()).join(lines);
252   }
253 }
254