View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.buildcache.its.junit;
20  
21  import java.io.IOException;
22  import java.lang.reflect.Field;
23  import java.lang.reflect.Method;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.nio.file.Paths;
27  import java.util.Comparator;
28  import java.util.stream.Stream;
29  
30  import org.apache.maven.it.VerificationException;
31  import org.apache.maven.it.Verifier;
32  import org.junit.jupiter.api.extension.BeforeAllCallback;
33  import org.junit.jupiter.api.extension.BeforeEachCallback;
34  import org.junit.jupiter.api.extension.ExtensionContext;
35  import org.junit.jupiter.api.extension.ParameterContext;
36  import org.junit.jupiter.api.extension.ParameterResolutionException;
37  import org.junit.jupiter.api.extension.ParameterResolver;
38  import org.junit.jupiter.api.parallel.ResourceLock;
39  import org.junit.jupiter.api.parallel.Resources;
40  
41  @ResourceLock(Resources.SYSTEM_PROPERTIES)
42  public class IntegrationTestExtension implements BeforeAllCallback, BeforeEachCallback, ParameterResolver {
43  
44      private static Path mavenHome;
45  
46      @Override
47      public void beforeAll(ExtensionContext context) throws IOException {
48          Path basedir;
49          String basedirstr = System.getProperty("maven.basedir");
50          if (basedirstr == null) {
51              if (Files.exists(Paths.get("target/maven3"))) {
52                  basedir = Paths.get("target/maven3");
53              } else if (Files.exists(Paths.get("target/maven4"))) {
54                  basedir = Paths.get("target/maven4");
55              } else {
56                  throw new IllegalStateException("Could not find maven home !");
57              }
58          } else {
59              basedir = Paths.get(basedirstr);
60          }
61          mavenHome = Files.list(basedir.toAbsolutePath())
62                  .filter(p -> Files.exists(p.resolve("bin/mvn")))
63                  .findAny()
64                  .orElseThrow(() -> new IllegalStateException("Could not find maven home"));
65          System.setProperty("maven.home", mavenHome.toString());
66          mavenHome.resolve("bin/mvn").toFile().setExecutable(true);
67  
68          deleteDir(Paths.get("target/build-cache/"));
69      }
70  
71      @Override
72      public void beforeEach(ExtensionContext context) throws Exception {
73          final Class<?> testClass = context.getRequiredTestClass();
74          final IntegrationTest test = testClass.getAnnotation(IntegrationTest.class);
75          final String rawProjectDir = test.value();
76          final String className = context.getRequiredTestClass().getSimpleName();
77          String methodName = context.getRequiredTestMethod().getName();
78          if (rawProjectDir == null) {
79              throw new IllegalStateException("@IntegrationTest must be set");
80          }
81          final Path testDir = Paths.get("target/mvn-cache-tests/" + className + "/" + methodName)
82                  .toAbsolutePath();
83          deleteDir(testDir);
84          Files.createDirectories(testDir);
85          final Path testExecutionDir;
86  
87          final Path testSrcDir = Paths.get(rawProjectDir).toAbsolutePath().normalize();
88          if (!Files.exists(testSrcDir)) {
89              throw new IllegalStateException(
90                      "@IntegrationTest(\"" + testSrcDir + "\") points at a path that does not exist: " + testSrcDir);
91          }
92          testExecutionDir = testDir.resolve("project");
93          try (Stream<Path> files = Files.walk(testSrcDir)) {
94              files.forEach(source -> {
95                  final Path dest = testExecutionDir.resolve(testSrcDir.relativize(source));
96                  try {
97                      if (Files.isDirectory(source)) {
98                          Files.createDirectories(dest);
99                      } else {
100                         Files.createDirectories(dest.getParent());
101                         Files.copy(source, dest);
102                     }
103                 } catch (IOException e) {
104                     throw new RuntimeException(e);
105                 }
106             });
107         }
108 
109         for (Field field : testClass.getDeclaredFields()) {
110             if (field.isAnnotationPresent(Inject.class)) {
111                 if (field.getType() == Verifier.class) {
112                     field.setAccessible(true);
113                     field.set(context.getRequiredTestInstance(), resolveParameter(null, context));
114                 }
115             }
116         }
117 
118         for (Method method : testClass.getDeclaredMethods()) {
119             if (method.isAnnotationPresent(BeforeEach.class)) {
120                 method.setAccessible(true);
121                 method.invoke(context.getRequiredTestInstance());
122             }
123         }
124     }
125 
126     @Override
127     public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
128             throws ParameterResolutionException {
129         return parameterContext.getParameter().getType() == Verifier.class;
130     }
131 
132     @Override
133     public Object resolveParameter(ParameterContext parameterContext, ExtensionContext context)
134             throws ParameterResolutionException {
135         try {
136             final IntegrationTest test = context.getRequiredTestClass().getAnnotation(IntegrationTest.class);
137             final String rawProjectDir = test.value();
138             if (rawProjectDir == null) {
139                 throw new IllegalStateException("value of @IntegrationTest must be set");
140             }
141 
142             final String className = context.getRequiredTestClass().getSimpleName();
143             String methodName = context.getRequiredTestMethod().getName();
144             final Path testDir = Paths.get("target/mvn-cache-tests/" + className + "/" + methodName)
145                     .toAbsolutePath();
146 
147             deleteDir(testDir);
148             Files.createDirectories(testDir);
149 
150             final Path testSrcDir = Paths.get(rawProjectDir).toAbsolutePath().normalize();
151             if (!Files.exists(testSrcDir)) {
152                 throw new IllegalStateException(
153                         "@IntegrationTest(\"" + testSrcDir + "\") points at a path that does not exist: " + testSrcDir);
154             }
155 
156             final Path testExecutionDir = testDir.resolve("project");
157             try (Stream<Path> files = Files.walk(testSrcDir)) {
158                 files.forEach(source -> {
159                     final Path dest = testExecutionDir.resolve(testSrcDir.relativize(source));
160                     try {
161                         if (Files.isDirectory(source)) {
162                             Files.createDirectories(dest);
163                         } else {
164                             Files.createDirectories(dest.getParent());
165                             Files.copy(source, dest);
166                         }
167                     } catch (IOException e) {
168                         throw new RuntimeException(e);
169                     }
170                 });
171             }
172 
173             Verifier verifier = new Verifier(testExecutionDir.toString(), true);
174             verifier.setLogFileName("../log.txt");
175             verifier.setSystemProperty("projectVersion", System.getProperty("projectVersion"));
176             verifier.setLocalRepo(System.getProperty("localRepo"));
177             return verifier;
178         } catch (VerificationException | IOException e) {
179             throw new ParameterResolutionException("Unable to create Verifier", e);
180         }
181     }
182 
183     public static Path deleteDir(Path dir) {
184         return deleteDir(dir, true);
185     }
186 
187     public static Path deleteDir(Path dir, boolean failOnError) {
188         if (Files.exists(dir)) {
189             try (Stream<Path> files = Files.walk(dir)) {
190                 files.sorted(Comparator.reverseOrder()).forEach(f -> deleteFile(f, failOnError));
191             } catch (Exception e) {
192                 throw new RuntimeException("Could not walk " + dir, e);
193             }
194         }
195         return dir;
196     }
197 
198     private static void deleteFile(Path f, boolean failOnError) {
199         try {
200             Files.delete(f);
201         } catch (Exception e) {
202             if (failOnError) {
203                 throw new RuntimeException("Could not delete " + f, e);
204             } else {
205                 System.err.println("Error deleting " + f + ": " + e);
206             }
207         }
208     }
209 }