001    package org.hackystat.telemetry.analyzer.reducer.impl;
002    
003    import java.util.List;
004    
005    import org.hackystat.dailyprojectdata.client.DailyProjectDataClient;
006    import org.hackystat.dailyprojectdata.resource.unittest.jaxb.MemberData;
007    import org.hackystat.dailyprojectdata.resource.unittest.jaxb.UnitTestDailyProjectData;
008    import org.hackystat.sensorbase.resource.projects.jaxb.Project;
009    import org.hackystat.telemetry.analyzer.model.TelemetryDataPoint;
010    import org.hackystat.telemetry.analyzer.model.TelemetryStream;
011    import org.hackystat.telemetry.analyzer.model.TelemetryStreamCollection;
012    import org.hackystat.telemetry.analyzer.reducer.TelemetryReducer;
013    import org.hackystat.telemetry.analyzer.reducer.TelemetryReducerException;
014    import org.hackystat.telemetry.analyzer.reducer.util.IntervalUtility;
015    import org.hackystat.telemetry.service.server.ServerProperties;
016    import org.hackystat.utilities.time.interval.Interval;
017    import org.hackystat.utilities.time.period.Day;
018    import org.hackystat.utilities.tstamp.Tstamp;
019    
020    
021    /**
022     * Returns a single stream providing UnitTest data. 
023     * <p>
024     * Options:
025     * <ol>
026     * <li> mode: One of 'TotalCount', 'SuccessCount', or 'FailureCount'. Default is 'TotalCount'. 
027     * <li> member: The project member whose unit test data is to be returned, or "*" for all members.
028     * <li> isCumulative: True or false. Default is false.
029     * </ol>
030     * 
031     * @author Hongbing Kou, Philip Johnson
032     */
033    public class UnitTestReducer implements TelemetryReducer { 
034     
035      /** Possible mode values. */
036      public enum Mode { TOTALCOUNT, SUCCESSCOUNT, FAILURECOUNT }
037      
038      /**
039       * Computes and returns the required telemetry streams object.
040       *
041       * @param project The project.
042       * @param dpdClient The DPD Client.
043       * @param interval The interval.
044       * @param options The optional parameters.
045       *
046       * @return Telemetry stream collection.
047       * @throws TelemetryReducerException If there is any error.
048       */
049      public TelemetryStreamCollection compute(Project project, DailyProjectDataClient dpdClient, 
050          Interval interval, String[] options) throws TelemetryReducerException {
051        Mode mode = Mode.TOTALCOUNT;
052        String member = null;
053        boolean isCumulative = false;
054        //process options
055        if (options.length > 3) {
056          throw new TelemetryReducerException("UnitTest reducer takes 3 optional parameters.");
057        }
058        if (options.length >= 1) {
059          try {
060            mode = Mode.valueOf(options[0].toUpperCase());
061          }
062          catch (Exception e) {
063            throw new TelemetryReducerException("Illegal mode value.", e);
064          }
065        }
066    
067        if (options.length >= 2) {
068          member = options[1];
069        }
070        
071        if (options.length >= 3) {
072          try {
073            isCumulative = Boolean.valueOf(options[2]);
074          }
075          catch (Exception e) {
076            throw new TelemetryReducerException("Illegal cumulative value.", e);
077          }
078        }
079        
080        // Find out the DailyProjectData host, throw error if not found.
081        String dpdHost = System.getProperty(ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY);
082        if (dpdHost == null) {
083          throw new TelemetryReducerException("Null DPD host in UnitTestReducer");
084        }
085    
086        // now get the telemetry stream. 
087        try {
088          TelemetryStream telemetryStream = this.getStream(dpdClient, project, interval,  
089              mode, member, isCumulative, null);
090          TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval);
091          streams.add(telemetryStream);
092          return streams;
093        } 
094        catch (Exception e) {
095          throw new TelemetryReducerException(e);
096        }
097      }
098    
099      /**
100       * Gets the telemetry stream.
101       * 
102       * @param dpdClient The DailyProjectData client we will contact for the data. 
103       * @param project The project.
104       * @param interval The interval.
105       * @param mode The mode (TOTALCOUNT, SUCCESSCOUNT, or FAILURECOUNT).
106       * @param member The member, or "*" for all members.
107       * @param isCumulative True for cumulative measure.
108       * @param streamTagValue The tag for the generated telemetry stream.
109       * 
110       * @return The telemetry stream as required.
111       * 
112       * @throws Exception If there is any error.
113       */
114      TelemetryStream getStream(DailyProjectDataClient dpdClient, 
115          Project project, Interval interval, Mode mode, 
116          String member, boolean isCumulative, Object streamTagValue) 
117            throws Exception {
118        TelemetryStream telemetryStream = new TelemetryStream(streamTagValue);
119        List<IntervalUtility.Period> periods = IntervalUtility.getPeriods(interval);
120        double cumulativeTestCount = 0;
121        
122        for (IntervalUtility.Period period : periods) {
123          Long value = this.getData(dpdClient, project, period.getStartDay(), period.getEndDay(),
124              mode, member);
125          
126          if (value != null) {
127            cumulativeTestCount += value;
128          }
129          
130          if (isCumulative) {
131            telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), 
132                cumulativeTestCount));        
133          }
134          else {
135            telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), value));
136          }
137        }
138        return telemetryStream;
139      }
140      
141      /**
142       * Returns a UnitTest value for the specified time interval, or null if no SensorData. 
143       * 
144       * @param dpdClient The DailyProjectData client we will use to get this data. 
145       * @param project The project.
146       * @param startDay The start day (inclusive).
147       * @param endDay The end day (inclusive).
148       * @param mode The mode.
149       * @param member The member email, or "*" for all members.
150       * @throws TelemetryReducerException If anything goes wrong.
151       *
152       * @return The UnitTest count, or null if there is no UnitTest SensorData for that time period. 
153       */
154      Long getData(DailyProjectDataClient dpdClient, Project project, Day startDay, Day endDay, 
155          Mode mode, String member) throws TelemetryReducerException {
156        long count = 0;
157        boolean hasData = false;
158        try {
159          // For each day in the interval... 
160          for (Day day = startDay; day.compareTo(endDay) <= 0; day = day.inc(1) ) {
161            // Get the DPD...
162            UnitTestDailyProjectData data = 
163              dpdClient.getUnitTest(project.getOwner(), project.getName(), Tstamp.makeTimestamp(day));
164            // Go through the DPD per-member data...
165            for (MemberData memberData : data.getMemberData()) {
166              if ((member == null) || "*".equals(member) || 
167                  (memberData.getMemberUri().endsWith(member))) {
168                hasData = true;
169                switch (mode) {
170                case TOTALCOUNT:
171                  count += memberData.getFailure().longValue() + memberData.getSuccess().longValue();
172                  break;
173                case SUCCESSCOUNT: 
174                  count += memberData.getSuccess().longValue();
175                  break;
176                case FAILURECOUNT: 
177                  count += memberData.getFailure().longValue();
178                  break;
179                default: 
180                  throw new TelemetryReducerException("Unknown mode: " + mode);
181                }
182              }
183            }
184          }
185        }
186        catch (Exception ex) {
187          throw new TelemetryReducerException(ex);
188        }
189    
190        //Return null if no data, the UnitTest data otherwise. 
191        return (hasData) ? Long.valueOf(count) : null; 
192      }
193    
194    }