1 /*******************************************************************************
2 * Copyright (c) 2000, 2009 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.test.internal.performance.results.utils;
12
13 import java.io.File;
14 import java.io.FileInputStream;
15 import java.io.FileNotFoundException;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.OutputStream;
20 import java.text.NumberFormat;
21 import java.text.ParseException;
22 import java.text.SimpleDateFormat;
23 import java.util.ArrayList;
24 import java.util.Calendar;
25 import java.util.Comparator;
26 import java.util.Date;
27 import java.util.GregorianCalendar;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.StringTokenizer;
31
32 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
33 import org.eclipse.test.internal.performance.results.db.BuildResults;
34 import org.eclipse.test.internal.performance.results.db.DB_Results;
35
36 /**
37 * Utility methods for statistics. Got from org.eclipse.test.performance
38 * framework
39 */
40 public final class Util implements IPerformancesConstants {
41
42 // Percentages
43 public static final NumberFormat PERCENTAGE_FORMAT = NumberFormat.getPercentInstance(Locale.US);
44 static {
45 PERCENTAGE_FORMAT.setMaximumFractionDigits(2);
46 }
47 public static final NumberFormat DOUBLE_FORMAT = NumberFormat.getNumberInstance(Locale.US);
48 static {
49 DOUBLE_FORMAT.setMaximumFractionDigits(2);
50 }
51
52 // Strings
53 public static final String LINE_SEPARATOR = System.getProperty("line.separator");
54
55 // Build prefixes
56 public static final List ALL_BUILD_PREFIXES = new ArrayList(3);
57 static {
58 ALL_BUILD_PREFIXES.add("I");
59 ALL_BUILD_PREFIXES.add("N");
60 ALL_BUILD_PREFIXES.add("M");
61 }
62 public static final List BUILD_PREFIXES = new ArrayList(2);
63 static {
64 BUILD_PREFIXES.add("I");
65 BUILD_PREFIXES.add("N");
66 }
67 public static final List MAINTENANCE_BUILD_PREFIXES = new ArrayList(2);
68 static {
69 MAINTENANCE_BUILD_PREFIXES.add("I");
70 MAINTENANCE_BUILD_PREFIXES.add("M");
71 }
72 public static final List BASELINE_BUILD_PREFIXES = new ArrayList(1);
73 static {
DB_Results.getDbBaselinePrefix()74 BASELINE_BUILD_PREFIXES.add(DB_Results.getDbBaselinePrefix());
75 }
76
77 // Milestones constants
78 private static String[] MILESTONES;
79 public static final BuildDateComparator BUILD_DATE_COMPARATOR = new BuildDateComparator();
80
81 static class BuildDateComparator implements Comparator {
compare(Object o1, Object o2)82 public int compare(Object o1, Object o2) {
83 String s1 = (String) o1;
84 String s2 = (String) o2;
85 return getBuildDate(s1).compareTo(getBuildDate(s2));
86 }
87 }
88
initMilestones()89 private static void initMilestones() {
90 String version = DB_Results.getDbVersion();
91
92 // Initialize reference version and database directory
93 char mainVersion = version.charAt(1);
94 char minorVersion = version.charAt(2);
95
96 // Initialize milestones
97 if (mainVersion == '3') {
98 switch (minorVersion) {
99 case '3':
100 case '4':
101 throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is no longer supported!");
102 case '5':
103 MILESTONES = V35_MILESTONES;
104 break;
105 case '6':
106 MILESTONES = V36_MILESTONES;
107 break;
108 default:
109 throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is not supported yet!");
110 }
111 } else {
112 throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is not supported yet!");
113 }
114 }
115
116 // Static information for time and date
117 public static final int ONE_MINUTE = 60000;
118 public static final long ONE_HOUR = 3600000L;
119 public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); //$NON-NLS-1$
120
121 /**
122 * Compute the student t-test values.
123 *
124 * @see "http://en.wikipedia.org/wiki/Student's_t-test"
125 *
126 * @param baselineResults The baseline build
127 * @param buildResults The current build
128 * @return The student t-test value as a double.
129 */
computeTTest(BuildResults baselineResults, BuildResults buildResults)130 public static double computeTTest(BuildResults baselineResults, BuildResults buildResults) {
131
132 double ref = baselineResults.getValue();
133 double val = buildResults.getValue();
134
135 double delta = ref - val;
136 long dfRef = baselineResults.getCount() - 1;
137 double sdRef = baselineResults.getDeviation();
138 long dfVal = buildResults.getCount() - 1;
139 double sdVal = buildResults.getDeviation();
140 // TODO if the stdev's are not sufficiently similar, we have to take a
141 // different approach
142
143 if (!Double.isNaN(sdRef) && !Double.isNaN(sdVal) && dfRef > 0 && dfVal > 0) {
144 long df = dfRef + dfVal;
145 double sp_square = (dfRef * sdRef * sdRef + dfVal * sdVal * sdVal) / df;
146
147 double se_diff = Math.sqrt(sp_square * (1.0 / (dfRef + 1) + 1.0 / (dfVal + 1)));
148 double t = Math.abs(delta / se_diff);
149 return t;
150 }
151
152 return -1;
153 }
154
155 /**
156 * Copy a file to another location.
157 *
158 * @param src the source file.
159 * @param dest the destination.
160 * @return <code>true</code> if the file was successfully copied,
161 * <code>false</code> otherwise.
162 */
copyFile(File src, File dest)163 public static boolean copyFile(File src, File dest) {
164
165 try {
166 InputStream in = new FileInputStream(src);
167 OutputStream out = new FileOutputStream(dest);
168 byte[] buf = new byte[1024];
169 int len;
170 while ((len = in.read(buf)) > 0) {
171 out.write(buf, 0, len);
172 }
173 in.close();
174 out.close();
175 } catch (FileNotFoundException e) {
176 e.printStackTrace();
177 return false;
178 } catch (IOException e) {
179 e.printStackTrace();
180 return false;
181 }
182 return true;
183 }
184
185 /**
186 * Copy a file content to another location.
187 *
188 * @param in the input stream.
189 * @param dest the destination.
190 * @return <code>true</code> if the file was successfully copied,
191 * <code>false</code> otherwise.
192 */
copyStream(InputStream in, File dest)193 public static boolean copyStream(InputStream in, File dest) {
194
195 try {
196 OutputStream out = new FileOutputStream(dest);
197 byte[] buf = new byte[1024];
198 int len;
199 while ((len = in.read(buf)) > 0) {
200 out.write(buf, 0, len);
201 }
202 in.close();
203 out.close();
204 } catch (FileNotFoundException e) {
205 e.printStackTrace();
206 return false;
207 } catch (IOException e) {
208 e.printStackTrace();
209 return false;
210 }
211 return true;
212 }
213
214 /**
215 * Return the build date as yyyyMMddHHmm.
216 *
217 * @param buildName The build name (e.g. I20090806-0100)
218 * @return The date as a string.
219 */
getBuildDate(String buildName)220 public static String getBuildDate(String buildName) {
221 return getBuildDate(buildName, DB_Results.getDbBaselinePrefix());
222 }
223
224 /**
225 * Return the build date as yyyyMMddHHmm.
226 *
227 * @param buildName The build name (e.g. I20090806-0100)
228 * @param baselinePrefix The baseline prefix (e.g. {@link DB_Results#getDbBaselinePrefix()})
229 * @return The date as a string.
230 */
getBuildDate(String buildName, String baselinePrefix)231 public static String getBuildDate(String buildName, String baselinePrefix) {
232
233 // Baseline name
234 if (baselinePrefix != null && buildName.startsWith(baselinePrefix)) {
235 int length = buildName.length();
236 return buildName.substring(length-12, length);
237 }
238
239 // Build name
240 char first = buildName.charAt(0);
241 if (first == 'N' || first == 'I' || first == 'M') { // TODO (frederic) should be buildIdPrefixes...
242 return buildName.substring(1, 9)+buildName.substring(10, 14);
243 }
244
245 // Try with date format
246 int length = buildName.length() - 12 /* length of date */;
247 for (int i=0; i<=length; i++) {
248 try {
249 String substring = i == 0 ? buildName : buildName.substring(i);
250 Util.DATE_FORMAT.parse(substring);
251 return substring; // if no exception is raised then the substring has a correct date format => return it
252 } catch(ParseException ex) {
253 // skip
254 }
255 }
256 return null;
257 }
258
259 /**
260 * Returns the date of the milestone corresponding at the given index.
261 *
262 * @param index The index of the milestone
263 * @return The date as a YYYYMMDD-hhmm string.
264 */
getMilestoneDate(int index)265 public static String getMilestoneDate(int index) {
266 int length = getMilestonesLength();
267 if (index >= length) return null;
268 int dash = MILESTONES[index].indexOf('-');
269 return MILESTONES[index].substring(dash+1);
270 }
271
272 /**
273 * Returns the milestone matching the given build name.
274 *
275 * @param buildName The name of the build
276 * @return The milestone as a string (e.g. M1)
277 */
getMilestone(String buildName)278 public static String getMilestone(String buildName) {
279 if (buildName != null && buildName.length() >= 12) {
280 int length = getMilestonesLength();
281 String buildDate = getBuildDate(buildName, DB_Results.getDbBaselinePrefix());
282 for (int i=0; i<length; i++) {
283 int start = MILESTONES[i].indexOf(buildDate);
284 if (start > 0) {
285 return MILESTONES[i];
286 }
287 }
288 }
289 return null;
290 }
291
292 /**
293 * Returns the name the milestone matching the given build name.
294 *
295 * @param buildName The name of the build
296 * @return The milestone name as a string (e.g. M1)
297 */
getMilestoneName(String buildName)298 public static String getMilestoneName(String buildName) {
299 if (buildName != null && buildName.length() >= 12) {
300 int length = getMilestonesLength();
301 String buildDate = getBuildDate(buildName, DB_Results.getDbBaselinePrefix());
302 for (int i=0; i<length; i++) {
303 int start = MILESTONES[i].indexOf(buildDate);
304 if (start > 0) {
305 return MILESTONES[i].substring(0, start - 1);
306 }
307 }
308 }
309 return null;
310 }
311
312 /**
313 * Returns whether the given build name is a milestone or not.
314 *
315 * @param buildName The build name
316 * @return <code>true</code> if the build name matches a milestone one,
317 * <code>false</code> otherwise.
318 */
isMilestone(String buildName)319 public static boolean isMilestone(String buildName) {
320 return getMilestoneName(buildName) != null;
321 }
322
323 /**
324 * Returns the name of the milestone which run after the given build name
325 * or <code>null</code> if there's no milestone since the build has run.
326 *
327 * @param buildName The build name
328 * @return <code>true</code> if the build name matches a milestone one,
329 * <code>false</code> otherwise.
330 */
getNextMilestone(String buildName)331 public static String getNextMilestone(String buildName) {
332 int length = getMilestonesLength();
333 String buildDate = getBuildDate(buildName);
334 for (int i=0; i<length; i++) {
335 String milestoneDate = MILESTONES[i].substring(MILESTONES[i].indexOf('-')+1);
336 if (milestoneDate.compareTo(buildDate) > 0) {
337 return milestoneDate;
338 }
339 }
340 return null;
341 }
342
343 /**
344 * Return the number of milestones.
345 *
346 * @return The number as an int
347 */
getMilestonesLength()348 public static int getMilestonesLength() {
349 if (MILESTONES == null) initMilestones();
350 int length = MILESTONES.length;
351 return length;
352 }
353
354 /**
355 * @deprecated
356 */
matchPattern(String name, String pattern)357 public static boolean matchPattern(String name, String pattern) {
358 if (pattern.equals("*")) return true; //$NON-NLS-1$
359 if (pattern.indexOf('*') < 0 && pattern.indexOf('?') < 0) {
360 pattern += "*"; //$NON-NLS-1$
361 }
362 StringTokenizer tokenizer = new StringTokenizer(pattern, "*?", true); //$NON-NLS-1$
363 int start = 0;
364 String previous = ""; //$NON-NLS-1$
365 while (tokenizer.hasMoreTokens()) {
366 String token = tokenizer.nextToken();
367 if (!token.equals("*") && !token.equals("?")) { //$NON-NLS-1$ //$NON-NLS-2$
368 if (previous.equals("*")) { //$NON-NLS-1$
369 int idx = name.substring(start).indexOf(token);
370 if (idx < 0) return false;
371 start += idx;
372 } else {
373 if (previous.equals("?")) start++; //$NON-NLS-1$
374 if (!name.substring(start).startsWith(token)) return false;
375 }
376 start += token.length();
377 }
378 previous = token;
379 }
380 if (previous.equals("*")) { //$NON-NLS-1$
381 return true;
382 } else if (previous.equals("?")) { //$NON-NLS-1$
383 return name.length() == start;
384 }
385 return name.endsWith(previous);
386 }
387
388 /**
389 * @deprecated
390 */
round(double value)391 public static double round(double value) {
392 return Math.round(value * 10000) / 10000.0;
393 }
394
395 /**
396 * @deprecated
397 */
round(double value, int precision)398 public static double round(double value, int precision) {
399 if (precision < 0) {
400 throw new IllegalArgumentException("Should have a precision at least greater than 0!");
401 }
402 if (precision == 0) return (long) Math.floor(value);
403 double factor = 10;
404 int n = 1;
405 while (n++ < precision)
406 factor *= 10;
407 return Math.round(value * factor) / factor;
408 }
409
410 /**
411 * Returns a string to display the given time as a duration
412 * formatted as "hh:mm:ss".
413 *
414 * @param time The time to format as a long.
415 * @return The formatted string.
416 */
timeChrono(long time)417 public static String timeChrono(long time) {
418 if (time < 1000) { // less than 1s
419 return "00:00:00"; //$NON-NLS-1$
420 }
421 StringBuffer buffer = new StringBuffer();
422 int seconds = (int) (time / 1000);
423 if (seconds < 60) {
424 buffer.append("00:00:"); //$NON-NLS-1$
425 if (seconds < 10) buffer.append('0');
426 buffer.append(seconds);
427 } else {
428 int minutes = seconds / 60;
429 if (minutes < 60) {
430 buffer.append("00:"); //$NON-NLS-1$
431 if (minutes < 10) buffer.append('0');
432 buffer.append(minutes);
433 buffer.append(':');
434 seconds = seconds % 60;
435 if (seconds < 10) buffer.append('0');
436 buffer.append(seconds);
437 } else {
438 int hours = minutes / 60;
439 if (hours < 10) buffer.append('0');
440 buffer.append(hours);
441 buffer.append(':');
442 minutes = minutes % 60;
443 if (minutes < 10) buffer.append('0');
444 buffer.append(minutes);
445 buffer.append(':');
446 seconds = seconds % 60;
447 if (seconds < 10) buffer.append('0');
448 buffer.append(seconds);
449 }
450 }
451 return buffer.toString();
452 }
453
454 /**
455 * Returns a string to display the given time as the hour of the day
456 * formatted as "hh:mm:ss".
457 *
458 * @param time The time to format as a long.
459 * @return The formatted string.
460 */
timeEnd(long time)461 public static String timeEnd(long time) {
462 GregorianCalendar calendar = new GregorianCalendar();
463 calendar.add(Calendar.SECOND, (int)(time/1000));
464 Date date = calendar.getTime();
465 SimpleDateFormat dateFormat = new SimpleDateFormat("KK:mm:ss"); //$NON-NLS-1$
466 return dateFormat.format(date);
467 }
468
469 /**
470 * Returns a string to display the given time as a duration
471 * formatted as:
472 * <ul>
473 * <li>"XXXms" if the duration is less than 0.1s (e.g. "543ms")</li>
474 * <li>"X.YYs" if the duration is less than 1s (e.g. "5.43s")</li>
475 * <li>"XX.Ys" if the duration is less than 1mn (e.g. "54.3s")</li>
476 * <li>"XXmn XXs" if the duration is less than 1h (e.g. "54mn 3s")</li>
477 * <li>"XXh XXmn XXs" if the duration is over than 1h (e.g. "5h 4mn 3s")</li>
478 * </ul>
479 *
480 * @param time The time to format as a long.
481 * @return The formatted string.
482 */
timeString(long time)483 public static String timeString(long time) {
484 NumberFormat format = NumberFormat.getInstance();
485 format.setMaximumFractionDigits(1);
486 StringBuffer buffer = new StringBuffer();
487 if (time == 0) {
488 // print nothing
489 } if (time < 100) { // less than 0.1s
490 buffer.append(time);
491 buffer.append("ms"); //$NON-NLS-1$
492 } else if (time < 1000) { // less than 1s
493 if ((time%100) != 0) {
494 format.setMaximumFractionDigits(2);
495 }
496 buffer.append(format.format(time/1000.0));
497 buffer.append("s"); //$NON-NLS-1$
498 } else if (time < Util.ONE_MINUTE) { // less than 1mn
499 if ((time%1000) == 0) {
500 buffer.append(time/1000);
501 } else {
502 buffer.append(format.format(time/1000.0));
503 }
504 buffer.append("s"); //$NON-NLS-1$
505 } else if (time < Util.ONE_HOUR) { // less than 1h
506 buffer.append(time/Util.ONE_MINUTE).append("mn "); //$NON-NLS-1$
507 long seconds = time%Util.ONE_MINUTE;
508 buffer.append(seconds/1000);
509 buffer.append("s"); //$NON-NLS-1$
510 } else { // more than 1h
511 long h = time / Util.ONE_HOUR;
512 buffer.append(h).append("h "); //$NON-NLS-1$
513 long m = (time % Util.ONE_HOUR) / Util.ONE_MINUTE;
514 buffer.append(m).append("mn "); //$NON-NLS-1$
515 long seconds = m%Util.ONE_MINUTE;
516 buffer.append(seconds/1000);
517 buffer.append("s"); //$NON-NLS-1$
518 }
519 return buffer.toString();
520 }
521
Util()522 private Util() {
523 // don't instantiate
524 }
525
526 /**
527 * Set the milestones.
528 *
529 * @param items The milestones list (e.g. {@link IPerformancesConstants#V35_MILESTONES}).
530 */
setMilestones(String[] items)531 public static void setMilestones(String[] items) {
532 MILESTONES = items;
533 }
534
535 /**
536 * Init the milestones from preferences
537 *
538 * @param preferences The preferences from which got milestones list
539 */
initMilestones(IEclipsePreferences preferences)540 public static void initMilestones(IEclipsePreferences preferences) {
541 int eclipseVersion = preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
542 String prefix = IPerformancesConstants.PRE_MILESTONE_BUILDS + "." + eclipseVersion;
543 int index = 0;
544 String milestone = preferences.get(prefix + index, null);
545 String[] milestones = new String[20];
546 while (milestone != null) {
547 milestones[index] = milestone;
548 index++;
549 milestone = preferences.get(prefix + index, null);
550 }
551 int length = milestones.length;
552 if (index < length) {
553 System.arraycopy(milestones, 0, milestones = new String[index], 0, index);
554 }
555 MILESTONES = milestones;
556 }
557 }
558