1   package com.atlassian.maven.plugins.amps;
2   
3   import com.atlassian.maven.plugins.amps.product.ProductHandler;
4   import com.atlassian.maven.plugins.amps.product.ProductHandlerFactory;
5   import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
6   import org.apache.maven.plugin.MojoExecutionException;
7   import org.apache.maven.project.MavenProject;
8   import org.jfrog.maven.annomojo.annotations.MojoComponent;
9   import org.jfrog.maven.annomojo.annotations.MojoGoal;
10  import org.jfrog.maven.annomojo.annotations.MojoParameter;
11  import org.jfrog.maven.annomojo.annotations.MojoRequiresDependencyResolution;
12  
13  import java.io.File;
14  import java.util.*;
15  
16  /**
17   * Run the integration tests against the webapp
18   */
19  @MojoGoal("integration-test")
20  @MojoRequiresDependencyResolution("test")
21  public class IntegrationTestMojo extends AbstractProductHandlerMojo
22  {
23      /**
24       * Pattern for to use to find integration tests.  Only used if no test groups are defined.
25       */
26      @MojoParameter(expression = "${functional.test.pattern}")
27      private String functionalTestPattern = "it/**";
28  
29      /**
30       * The directory containing generated test classes of the project being tested.
31       */
32      @MojoParameter(expression = "${project.build.testOutputDirectory}", required = true)
33      private File testClassesDirectory;
34  
35      /**
36       * The list of specific test groups to execute
37       */
38      @MojoParameter
39      private List<TestGroup> testGroups = new ArrayList<TestGroup>();
40  
41      /**
42       * A comma separated list of test groups to run.  If not specified, all
43       * test groups are run.
44       */
45      @MojoParameter(expression = "${testGroups}")
46      private String configuredTestGroupsToRun;
47      
48      /**
49       * Whether the reference application will not be started or not
50       */
51      @MojoParameter(expression = "${no.webapp}", defaultValue = "false")
52      private boolean noWebapp = false;
53  
54      @MojoComponent
55      private ArtifactHandlerManager artifactHandlerManager;
56  
57      @MojoParameter(expression="${maven.test.skip}", defaultValue = "false")
58      private boolean testsSkip = false;
59  
60      @MojoParameter(expression="${skipTests}", defaultValue = "false")
61      private boolean skipTests = false;
62  
63      private static final String NO_TEST_GROUP = "__no_test_group__";
64      protected void doExecute() throws MojoExecutionException
65      {
66          final MavenProject project = getMavenContext().getProject();
67  
68          // workaround for MNG-1682/MNG-2426: force maven to install artifact using the "jar" handler
69          project.getArtifact().setArtifactHandler(artifactHandlerManager.getArtifactHandler("jar"));
70  
71          if (!new File(testClassesDirectory, "it").exists())
72          {
73              getLog().info("No integration tests found");
74              return;
75          }
76  
77          if (skipTests || testsSkip)
78          {
79              getLog().info("Integration tests skipped");
80              return;
81          }
82  
83          final MavenGoals goals = getMavenGoals();
84          final String pluginJar = targetDirectory.getAbsolutePath() + "/" + finalName + ".jar";
85  
86          final Set<String> configuredTestGroupIds = getTestGroupIds();
87          if (configuredTestGroupIds.isEmpty())
88          {
89              runTestsForTestGroup(NO_TEST_GROUP, goals, pluginJar, copy(systemPropertyVariables));
90          }
91          else if (configuredTestGroupsToRun != null)
92          {
93              String[] testGroupIdsToRun = configuredTestGroupsToRun.split(",");
94              
95              // fail fast if one of the test groups does not exist
96              for (String testGroupId : testGroupIdsToRun)
97              {
98                  if (!configuredTestGroupIds.contains(testGroupId))
99                  {
100                     throw new MojoExecutionException("Test group " + testGroupId + " does not exist");
101                 }
102             }
103             // now run the tests
104             for (String testGroupId : testGroupIdsToRun)
105             {
106                 runTestsForTestGroup(testGroupId, goals, pluginJar, copy(systemPropertyVariables));
107             }
108         }
109         else
110         {
111             for (String testGroupId : configuredTestGroupIds)
112             {
113                 runTestsForTestGroup(testGroupId, goals, pluginJar, copy(systemPropertyVariables));
114             }
115         }
116     }
117 
118     private Map<String,Object> copy(Map<String,Object> systemPropertyVariables)
119     {
120         return new HashMap<String,Object>(systemPropertyVariables);
121     }
122 
123     /**
124      * Returns product-specific properties to pass to the container during
125      * integration testing. Default implementation does nothing.
126      * @param product the {@code Product} object to use
127      * @return a {@code Map} of properties to add to the system properties passed
128      * to the container
129      */
130     protected Map<String, String> getProductFunctionalTestProperties(Product product)
131     {
132         return Collections.emptyMap();
133     }
134 
135     private Set<String> getTestGroupIds() throws MojoExecutionException
136     {
137         Set<String> ids = new HashSet<String>();
138 
139         //ids.addAll(ProductHandlerFactory.getIds());
140         for (TestGroup group : testGroups)
141         {
142             ids.add(group.getId());
143         }
144 
145         return ids;
146     }
147 
148     private List<String> getProductIdsForTestGroup(String testGroupId) throws MojoExecutionException
149     {
150         List<String> productIds = new ArrayList<String>();
151         if (NO_TEST_GROUP.equals(testGroupId))
152         {
153             productIds.add(getProductId());
154         }
155 
156         for (TestGroup group : testGroups)
157         {
158             if (group.getId().equals(testGroupId))
159             {
160                 productIds.addAll(group.getProductIds());
161             }
162         }
163         if (ProductHandlerFactory.getIds().contains(testGroupId) && !productIds.contains(testGroupId))
164         {
165             productIds.add(testGroupId);
166         }
167 
168         if (productIds.isEmpty())
169         {
170             throw new MojoExecutionException("Unknown test group id");
171         }
172 
173         return productIds;
174     }
175 
176     private void runTestsForTestGroup(String testGroupId, MavenGoals goals, String pluginJar, Map<String,Object> systemProperties) throws MojoExecutionException
177     {
178         List<String> includes = getIncludesForTestGroup(testGroupId);
179         List<String> excludes = getExcludesForTestGroup(testGroupId);
180         List<String> productIds = getProductIdsForTestGroup(testGroupId);
181 
182         // Create a container object to hold product-related stuff
183         List<TestGroupProductExecution> products = new ArrayList<TestGroupProductExecution>();
184         int dupCounter = 0;
185         Set<String> uniqueProductIds = new HashSet<String>();
186         for (String productId : productIds)
187         {
188             Product ctx = getProductContexts(goals).get(productId);
189             if (ctx == null)
190             {
191                 throw new MojoExecutionException("The test group '" + testGroupId + "' refers to a product '" + productId
192                     + "' that doesn't have an associated <product> configuration.");
193             }
194             ProductHandler productHandler = ProductHandlerFactory.create(ctx.getId(), getMavenContext().getProject(), goals, getLog());
195 
196             // Give unique ids to duplicate product instances
197             if (uniqueProductIds.contains(productId))
198             {
199                 ctx.setInstanceId(productId + "-" + dupCounter++);
200             }
201             else
202             {
203                 uniqueProductIds.add(productId);
204             }
205             products.add(new TestGroupProductExecution(ctx, productHandler));
206         }
207 
208         // Install the plugin in each product and start it
209         for (TestGroupProductExecution testGroupProductExecution : products)
210         {
211             ProductHandler productHandler = testGroupProductExecution.getProductHandler();
212             Product product = testGroupProductExecution.getProduct();
213             product.setInstallPlugin(installPlugin);
214 
215             int actualHttpPort = 0;
216             if (!noWebapp)
217             {
218                 actualHttpPort = productHandler.start(product);
219             }
220 
221             if (products.size() == 1)
222             {
223                 systemProperties.put("http.port", String.valueOf(actualHttpPort));
224                 systemProperties.put("context.path", product.getContextPath());
225             }
226 
227             String baseUrl = MavenGoals.getBaseUrl(product.getServer(), actualHttpPort, product.getContextPath());
228             // hard coded system properties...
229             systemProperties.put("http." + product.getInstanceId() + ".port", String.valueOf(actualHttpPort));
230             systemProperties.put("context." + product.getInstanceId() + ".path", product.getContextPath());
231             systemProperties.put("http." + product.getInstanceId() + ".url", MavenGoals.getBaseUrl(product.getServer(), actualHttpPort, product.getContextPath()));
232 
233             systemProperties.put("baseurl." + product.getInstanceId(), baseUrl);
234             systemProperties.put("plugin.jar", pluginJar);
235 
236             // yes, this means you only get one base url if multiple products, but that is what selenium would expect
237             if (!systemProperties.containsKey("baseurl"))
238             {
239                 systemProperties.put("baseurl", baseUrl);
240             }
241             
242             systemProperties.put("homedir." + product.getInstanceId(), productHandler.getHomeDirectory(product).getAbsolutePath());
243             if (!systemProperties.containsKey("homedir"))
244             {
245                 systemProperties.put("homedir", productHandler.getHomeDirectory(product).getAbsolutePath());
246             }
247             
248             systemProperties.putAll(getProductFunctionalTestProperties(product));
249         }
250         systemProperties.put("testGroup", testGroupId);
251         systemProperties.putAll(getTestGroupSystemProperties(testGroupId));
252 
253         // Actually run the tests
254         goals.runTests("group-" + testGroupId, containerId, includes, excludes, systemProperties, targetDirectory);
255 
256         // Shut all products down
257         for (TestGroupProductExecution testGroupProductExecution : products)
258         {
259             ProductHandler productHandler = testGroupProductExecution.getProductHandler();
260             Product product = testGroupProductExecution.getProduct();
261             if (!noWebapp)
262             {
263                 productHandler.stop(product);
264             }
265         }
266     }
267 
268     private Map<String, String> getTestGroupSystemProperties(String testGroupId)
269     {
270         if (NO_TEST_GROUP.equals(testGroupId))
271         {
272             return Collections.emptyMap();
273         }
274 
275         for (TestGroup group : testGroups)
276         {
277             if (group.getId().equals(testGroupId))
278             {
279                 return group.getSystemProperties();
280             }
281         }
282         return Collections.emptyMap();
283     }
284 
285     private List<String> getIncludesForTestGroup(String testGroupId)
286     {
287         if (NO_TEST_GROUP.equals(testGroupId))
288         {
289             return Collections.singletonList(functionalTestPattern);
290         }
291         else
292         {
293             for (TestGroup group : testGroups)
294             {
295                 if (group.getId().equals(testGroupId))
296                 {
297                     return group.getIncludes();
298                 }
299             }
300         }
301         return Collections.singletonList(functionalTestPattern);
302     }
303 
304 
305     private List<String> getExcludesForTestGroup(String testGroupId)
306     {
307         if (NO_TEST_GROUP.equals(testGroupId))
308         {
309             return Collections.emptyList();
310         }
311         else
312         {
313             for (TestGroup group : testGroups)
314             {
315                 if (group.getId().equals(testGroupId))
316                 {
317                     return group.getExcludes();
318                 }
319             }
320         }
321         return Collections.emptyList();
322     }
323 
324     /**
325      * The execution context for a product in a test group
326      */
327     private static class TestGroupProductExecution
328     {
329         private final Product product;
330         private final ProductHandler productHandler;
331 
332         public TestGroupProductExecution(Product product, ProductHandler productHandler)
333         {
334             this.product = product;
335             this.productHandler = productHandler;
336         }
337 
338         public ProductHandler getProductHandler()
339         {
340             return productHandler;
341         }
342 
343         public Product getProduct()
344         {
345             return product;
346         }
347     }
348 }