View Javadoc

1   package com.google.code.jetm.maven;
2   
3   import static org.fest.assertions.Assertions.assertThat;
4   
5   import java.io.File;
6   import java.io.FileReader;
7   import java.io.IOException;
8   import java.net.URL;
9   import java.text.DecimalFormat;
10  import java.util.Arrays;
11  import java.util.Collection;
12  import java.util.LinkedList;
13  import java.util.List;
14  import java.util.Properties;
15  import java.util.ResourceBundle;
16  
17  import org.apache.maven.shared.invoker.InvocationResult;
18  import org.apache.maven.shared.test.plugin.BuildTool;
19  import org.codehaus.plexus.util.FileUtils;
20  import org.junit.After;
21  import org.junit.AfterClass;
22  import org.junit.Before;
23  import org.junit.BeforeClass;
24  import org.junit.Test;
25  import org.openqa.selenium.By;
26  import org.openqa.selenium.WebDriver;
27  import org.openqa.selenium.WebElement;
28  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
29  
30  import com.google.code.jetm.maven.internal.SimpleAggregate;
31  import com.google.code.jetm.reporting.xml.XmlAggregateBinder;
32  
33  import etm.core.aggregation.Aggregate;
34  
35  /**
36   * Integration tests for the timing report mojo.
37   * 
38   * @author jrh3k5
39   * 
40   */
41  
42  public class TimingReportMojoITest {
43      private static final Properties originalSystemProperties = System.getProperties();
44      private final List<String> cleanTestSite = Arrays.asList("clean", "test", "site");
45      private WebDriver driver;
46      private BuildTool build;
47  
48      /**
49       * Set the {@code $maven.home} value in the system properties for the
50       * {@link BuildTool} object.
51       * 
52       * @throws Exception
53       *             If any errors occur during the setup.
54       */
55      @BeforeClass
56      public static void setUpBeforeClass() throws Exception {
57          final ResourceBundle systemPropsBundle = ResourceBundle.getBundle("system");
58          System.setProperty("maven.home", systemPropsBundle.getString("maven.home"));
59      }
60  
61      /**
62       * Create and initialize a {@link BuildTool} object to invoke Maven.
63       * 
64       * @throws Exception
65       *             If any errors occur during the setup.
66       */
67      @Before
68      public void setUp() throws Exception {
69          build = new BuildTool();
70          build.initialize();
71  
72          driver = new HtmlUnitDriver();
73      }
74  
75      /**
76       * Clean up resources used by the (possibly) instantiated {@link BuildTool}
77       * object.
78       * 
79       * @throws Exception
80       *             If any errors occur during the teardown.
81       */
82      @After
83      public void tearDown() throws Exception {
84          if (build != null)
85              build.dispose();
86  
87          if (driver != null)
88              driver.close();
89      }
90  
91      /**
92       * Restore the system properties to their original values.
93       * 
94       * @throws Exception
95       *             If any errors occur during the teardown.
96       */
97      @AfterClass
98      public static void tearDownAfterClass() throws Exception {
99          System.setProperties(originalSystemProperties);
100     }
101 
102     /**
103      * Test the creation of an empty report.
104      * 
105      * @throws Exception
106      *             If any errors occur during the test run.
107      */
108     @Test
109     public void testEmptyReport() throws Exception {
110         final String projectName = "empty-project";
111         final File logFile = new File("target/failsafe-reports/testEmptyProject-maven.log");
112         final InvocationResult result = build.executeMaven(getPom(projectName), null, cleanTestSite, logFile);
113         assertThat(result.getExitCode()).isZero();
114 
115         driver.get(getSiteIndexLocation(projectName));
116         openJetmTimingReport(driver);
117         assertThat(driver.findElement(By.id("contentBox")).getText()).contains("There are no JETM timings available for reporting.");
118     }
119 
120     /**
121      * Test the creation of a report for a demo project.
122      * 
123      * @throws Exception
124      *             If any errors occur during the test run.
125      */
126     @SuppressWarnings("unchecked")
127     @Test
128     public void testDemoProject() throws Exception {
129         final String projectName = "demo-project";
130         final File logFile = new File("target/failsafe-reports/testDemoProject-maven.log");
131         final InvocationResult result = build.executeMaven(getPom(projectName), null, cleanTestSite, logFile);
132         assertThat(result.getExitCode()).isZero();
133 
134         driver.get(getSiteIndexLocation(projectName));
135         openJetmTimingReport(driver);
136 
137         final Collection<Aggregate> reportData = getAggregateData();
138         for (File reportFile : (List<File>) FileUtils.getFiles(FileUtils.toFile(getClass().getResource("/example-projects/" + projectName + "/target/jetm")), "**/*.xml", null, true)) {
139             final Collection<Aggregate> aggregates = getAggregates(reportFile);
140             // Find the entry for the file
141             assertHasSection(reportFile.getName());
142             // Make sure the data's in the report
143             assertThat(reportData).contains(aggregates.toArray());
144         }
145     }
146 
147     /**
148      * Assert that the section by the given name exists in the report.
149      * 
150      * @param sectionName
151      *            The section name whose existence is to be verified.
152      */
153     private void assertHasSection(String sectionName) {
154         for (WebElement element : driver.findElements(By.tagName("h4")))
155             if (sectionName.equals(element.getText()))
156                 return;
157     
158         throw new IllegalArgumentException("No section name found: " + sectionName + ". Page source: " + driver.getPageSource());
159     }
160 
161     /**
162      * Parse aggregate data from the report.
163      * 
164      * @return A {@link Collection} of {@link Aggregate} objects representing
165      *         the data in the report.
166      */
167     private Collection<Aggregate> getAggregateData() {
168         final Collection<Aggregate> aggregates = new LinkedList<Aggregate>();
169         final List<WebElement> htmlRows = driver.findElements(By.tagName("tr"));
170         for (WebElement htmlRow : htmlRows) {
171             final List<WebElement> htmlCells = htmlRow.findElements(By.tagName("td"));
172             if (htmlCells.size() == 6) {
173                 final double average = Double.parseDouble(htmlCells.get(1).getText());
174                 final double min = Double.parseDouble(htmlCells.get(3).getText());
175                 final double max = Double.parseDouble(htmlCells.get(4).getText());
176                 final double total = Double.parseDouble(htmlCells.get(5).getText());
177                 final long measurements = Long.parseLong(htmlCells.get(2).getText());
178                 aggregates.add(new SimpleAggregate(htmlCells.get(0).getText(), average, min, max, measurements, total));
179             }
180         }
181         return aggregates;
182     }
183 
184     /**
185      * Get the aggregate data from the given file.
186      * 
187      * @param aggregateData
188      *            A {@link File} containing the raw aggregate data to be
189      *            collected.
190      * @return A {@link Collection} of {@link Aggregate} data parsed from the
191      *         given file and rounded to a precision as displayed in the report.
192      * @throws IOException
193      *             If any errors occur during the collection of the data.
194      */
195     private Collection<Aggregate> getAggregates(File aggregateData) throws IOException {
196         final FileReader reader = new FileReader(aggregateData);
197         try {
198             final Collection<Aggregate> aggregates = new LinkedList<Aggregate>();
199             for (Aggregate original : new XmlAggregateBinder().unbind(reader)) {
200                 /*
201                  * Divide each by a thousand to convert from milliseconds to
202                  * seconds
203                  */
204                 final double average = round(original.getAverage() * 0.001);
205                 final double min = round(original.getMin() * 0.001);
206                 final double max = round(original.getMax() * 0.001);
207                 final double total = round(original.getTotal() * 0.001);
208                 aggregates.add(new SimpleAggregate(original.getName(), average, min, max, original.getMeasurements(), total));
209             }
210             return aggregates;
211         } finally {
212             reader.close();
213         }
214     }
215 
216     /**
217      * Retrieve a POM file.
218      * 
219      * @param artifactId
220      *            The artifact ID of the project whose POM is to be retrieved.
221      * @return A {@link File} reference to the given artifact ID's POM.
222      * @throws IllegalArgumentException
223      *             If the POM cannot be found.
224      */
225     private File getPom(String artifactId) {
226         final URL pomUrl = getClass().getResource("/example-projects/" + artifactId + "/pom.xml");
227         if (pomUrl == null)
228             throw new IllegalArgumentException("No POM found for artifact: " + artifactId);
229         return FileUtils.toFile(pomUrl);
230     }
231 
232     /**
233      * Get the location of the index.html for a Maven project's site.
234      * 
235      * @param artifactId
236      *            The artifact ID of the project whose site index is to be
237      *            retrieved.
238      * @return A {@code String} representing the absolute location of the site
239      *         index.
240      * @throws IllegalArgumentException
241      *             If the site index cannot be found.
242      */
243     private String getSiteIndexLocation(String artifactId) {
244         final URL pomUrl = getClass().getResource("/example-projects/" + artifactId + "/target/site/index.html");
245         if (pomUrl == null)
246             throw new IllegalArgumentException("No index.html found for artifact: " + artifactId);
247         return pomUrl.toExternalForm();
248     }
249 
250     /**
251      * Open the JETM timing report.
252      * 
253      * @param driver
254      *            The {@link WebDriver} to use.
255      */
256     private void openJetmTimingReport(WebDriver driver) {
257         driver.findElement(By.linkText("Project Reports")).click();
258         driver.findElement(By.linkText("JETM Timing Report")).click();
259     }
260 
261     /**
262      * Round a floating point value to two decimal places.
263      * 
264      * @param value
265      *            The decimal value to be rounded.
266      * @return The given floating point value, rounded to two places.
267      */
268     private double round(double value) {
269         return Double.parseDouble(new DecimalFormat("0.00").format(value));
270     }
271 }