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.model; 12 13 import java.util.ArrayList; 14 import java.util.List; 15 import java.util.Vector; 16 17 import org.eclipse.core.runtime.IAdaptable; 18 import org.eclipse.core.runtime.preferences.IEclipsePreferences; 19 import org.eclipse.core.runtime.preferences.InstanceScope; 20 import org.eclipse.jface.resource.ImageDescriptor; 21 import org.eclipse.swt.graphics.Image; 22 import org.eclipse.test.internal.performance.eval.StatisticsUtil; 23 import org.eclipse.test.internal.performance.results.db.AbstractResults; 24 import org.eclipse.test.internal.performance.results.db.BuildResults; 25 import org.eclipse.test.internal.performance.results.db.ConfigResults; 26 import org.eclipse.test.internal.performance.results.db.DB_Results; 27 import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants; 28 import org.eclipse.test.internal.performance.results.utils.Util; 29 import org.eclipse.ui.ISharedImages; 30 import org.eclipse.ui.PlatformUI; 31 import org.eclipse.ui.model.IWorkbenchAdapter; 32 import org.eclipse.ui.views.properties.ComboBoxPropertyDescriptor; 33 import org.eclipse.ui.views.properties.IPropertyDescriptor; 34 import org.eclipse.ui.views.properties.IPropertySource; 35 import org.eclipse.ui.views.properties.PropertyDescriptor; 36 import org.eclipse.ui.views.properties.TextPropertyDescriptor; 37 import org.osgi.service.prefs.BackingStoreException; 38 39 /** 40 * An Organization Element 41 */ 42 public abstract class ResultsElement implements IAdaptable, IPropertySource, IWorkbenchAdapter, Comparable { 43 44 // Image descriptors 45 private static final ISharedImages WORKBENCH_SHARED_IMAGES = PlatformUI.getWorkbench().getSharedImages(); 46 public static final Image ERROR_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_OBJS_ERROR_TSK); 47 public static final ImageDescriptor ERROR_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJS_ERROR_TSK); 48 public static final Image WARN_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_OBJS_WARN_TSK); 49 public static final ImageDescriptor WARN_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJS_WARN_TSK); 50 public static final Image INFO_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_OBJS_INFO_TSK); 51 public static final ImageDescriptor INFO_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK); 52 public static final Image HELP_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_LCL_LINKTO_HELP); 53 public static final ImageDescriptor HELP_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_LCL_LINKTO_HELP); 54 public static final ImageDescriptor FOLDER_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER); 55 public static final ImageDescriptor CONNECT_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_ELCL_SYNCED); 56 57 // Model 58 ResultsElement parent; 59 AbstractResults results; 60 ResultsElement[] children; 61 String name; 62 int status = -1; 63 64 // Stats 65 double[] statistics; 66 67 // Status constants 68 // state 69 static final int UNKNOWN = 0x01; 70 static final int UNREAD = 0x02; 71 static final int READ = 0x04; 72 static final int MISSING = 0x08; 73 public static final int STATE_MASK = 0x0F; 74 // info 75 static final int SMALL_VALUE = 0x0010; 76 static final int STUDENT_TTEST = 0x0020; 77 public static final int INFO_MASK = 0x0030; 78 // warning 79 static final int NO_BASELINE = 0x0040; 80 static final int SINGLE_RUN = 0x0080; 81 static final int BIG_ERROR = 0x0100; 82 static final int NOT_STABLE = 0x0200; 83 static final int NOT_RELIABLE = 0x0400; 84 public static final int WARNING_MASK = 0x0FC0; 85 // error 86 static final int BIG_DELTA = 0x1000; 87 public static final int ERROR_MASK = 0xF000; 88 89 // Property descriptors 90 static final String P_ID_STATUS_INFO = "ResultsElement.status_info"; //$NON-NLS-1$ 91 static final String P_ID_STATUS_WARNING = "ResultsElement.status_warning"; //$NON-NLS-1$ 92 static final String P_ID_STATUS_ERROR = "ResultsElement.status_error"; //$NON-NLS-1$ 93 static final String P_ID_STATUS_COMMENT = "ResultsElement.status_comment"; //$NON-NLS-1$ 94 95 static final String P_STR_STATUS_INFO = " info"; //$NON-NLS-1$ 96 static final String P_STR_STATUS_WARNING = "warning"; //$NON-NLS-1$ 97 static final String P_STR_STATUS_ERROR = "error"; //$NON-NLS-1$ 98 static final String P_STR_STATUS_COMMENT = "comment"; //$NON-NLS-1$ 99 static final String[] NO_VALUES = new String[0]; 100 101 private static Vector DESCRIPTORS; 102 static final TextPropertyDescriptor COMMENT_DESCRIPTOR = new TextPropertyDescriptor(P_ID_STATUS_COMMENT, P_STR_STATUS_COMMENT); 103 static final TextPropertyDescriptor ERROR_DESCRIPTOR = new TextPropertyDescriptor(P_ID_STATUS_ERROR, P_STR_STATUS_ERROR); 104 static Vector initDescriptors(int status) { 105 DESCRIPTORS = new Vector(); 106 // Status category 107 DESCRIPTORS.add(getInfosDescriptor(status)); 108 DESCRIPTORS.add(getWarningsDescriptor(status)); 109 DESCRIPTORS.add(ERROR_DESCRIPTOR); 110 ERROR_DESCRIPTOR.setCategory("Status"); 111 // Survey category 112 DESCRIPTORS.add(COMMENT_DESCRIPTOR); 113 COMMENT_DESCRIPTOR.setCategory("Survey"); 114 return DESCRIPTORS; 115 } 116 static Vector getDescriptors() { 117 return DESCRIPTORS; 118 } 119 static ComboBoxPropertyDescriptor getInfosDescriptor(int status) { 120 List list = new ArrayList(); 121 if ((status & SMALL_VALUE) != 0) { 122 list.add("Some builds have tests with small values"); 123 } 124 if ((status & STUDENT_TTEST) != 0) { 125 list.add("Some builds have student-t test error over the threshold"); 126 } 127 String[] infos = new String[list.size()]; 128 if (list.size() > 0) { 129 list.toArray(infos); 130 } 131 ComboBoxPropertyDescriptor infoDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_INFO, P_STR_STATUS_INFO, infos); 132 infoDescriptor.setCategory("Status"); 133 return infoDescriptor; 134 } 135 static PropertyDescriptor getWarningsDescriptor(int status) { 136 List list = new ArrayList(); 137 if ((status & BIG_ERROR) != 0) { 138 list.add("Some builds have tests with error over 3%"); 139 } 140 if ((status & NOT_RELIABLE) != 0) { 141 list.add("Some builds have no reliable tests"); 142 } 143 if ((status & NOT_STABLE) != 0) { 144 list.add("Some builds have no stable tests"); 145 } 146 if ((status & NO_BASELINE) != 0) { 147 list.add("Some builds have no baseline to compare with"); 148 } 149 if ((status & SINGLE_RUN) != 0) { 150 list.add("Some builds have single run tests"); 151 } 152 String[] warnings = new String[list.size()]; 153 if (list.size() > 0) { 154 list.toArray(warnings); 155 } 156 ComboBoxPropertyDescriptor warningDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_WARNING, P_STR_STATUS_WARNING, warnings); 157 warningDescriptor.setCategory("Status"); 158 return warningDescriptor; 159 } 160 161 ResultsElement() { 162 } 163 164 ResultsElement(AbstractResults results, ResultsElement parent) { 165 this.parent = parent; 166 this.results = results; 167 } 168 169 ResultsElement(String name, ResultsElement parent) { 170 this.parent = parent; 171 this.name = name; 172 } 173 174 public int compareTo(Object o) { 175 if (this.results == null) { 176 if (o instanceof ResultsElement && this.name != null) { 177 ResultsElement element = (ResultsElement) o; 178 return this.name.compareTo(element.getName()); 179 } 180 return -1; 181 } 182 if (o instanceof ResultsElement) { 183 return this.results.compareTo(((ResultsElement)o).results); 184 } 185 return -1; 186 } 187 188 abstract ResultsElement createChild(AbstractResults testResults); 189 190 /* (non-Javadoc) 191 * Method declared on IAdaptable 192 */ 193 public Object getAdapter(Class adapter) { 194 if (adapter == IPropertySource.class) { 195 return this; 196 } 197 if (adapter == IWorkbenchAdapter.class) { 198 return this; 199 } 200 return null; 201 } 202 203 /** 204 * Iterate the element children. 205 */ 206 public ResultsElement[] getChildren() { 207 if (this.results == null) { 208 return new ResultsElement[0]; 209 } 210 if (this.children == null) { 211 initChildren(); 212 } 213 return this.children; 214 } 215 216 /* (non-Javadoc) 217 * Method declared on IWorkbenchAdapter 218 */ 219 public Object[] getChildren(Object o) { 220 if (this.results == null) { 221 return new Object[0]; 222 } 223 if (this.children == null) { 224 initChildren(); 225 } 226 return this.children; 227 } 228 229 /* (non-Javadoc) 230 * Method declared on IPropertySource 231 */ 232 public Object getEditableValue() { 233 return this; 234 } 235 236 final String getId() { 237 return getId(new StringBuffer()).toString(); 238 } 239 240 private StringBuffer getId(StringBuffer buffer) { 241 if (this.parent != null) { 242 return this.parent.getId(buffer).append('/').append(getName()); 243 } 244 return buffer.append(DB_Results.getDbName()); 245 } 246 247 /* (non-Javadoc) 248 * Method declared on IWorkbenchAdapter 249 */ 250 public ImageDescriptor getImageDescriptor(Object object) { 251 if (object instanceof ResultsElement) { 252 ResultsElement resultsElement = (ResultsElement) object; 253 // DEBUG 254 // if (resultsElement.getName().equals("I20090806-0100")) { 255 // if (resultsElement.results != null) { 256 // String toString = resultsElement.results.getParent().toString(); 257 // String toString = resultsElement.results.toString(); 258 // if (toString.indexOf("testStoreExists")>0 && toString.indexOf("eplnx2")>0) { 259 // System.out.println("stop"); 260 // } 261 // } 262 // } 263 int elementStatus = resultsElement.getStatus(); 264 if (elementStatus == MISSING) { 265 return HELP_IMAGE_DESCRIPTOR; 266 } 267 if ((elementStatus & ResultsElement.ERROR_MASK) != 0) { 268 return ERROR_IMAGE_DESCRIPTOR; 269 } 270 if ((elementStatus & ResultsElement.WARNING_MASK) != 0) { 271 return WARN_IMAGE_DESCRIPTOR; 272 } 273 if ((elementStatus & ResultsElement.INFO_MASK) != 0) { 274 return INFO_IMAGE_DESCRIPTOR; 275 } 276 } 277 return null; 278 } 279 280 /* (non-Javadoc) 281 * Method declared on IWorkbenchAdapter 282 */ 283 public String getLabel(Object o) { 284 return getName(); 285 } 286 287 /** 288 * Returns the name 289 */ 290 public String getName() { 291 if (this.name == null && this.results != null) { 292 this.name = this.results.getName(); 293 } 294 return this.name; 295 } 296 297 /** 298 * Returns the parent 299 */ 300 public Object getParent(Object o) { 301 return this.parent; 302 } 303 304 /* (non-Javadoc) 305 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors() 306 */ 307 public IPropertyDescriptor[] getPropertyDescriptors() { 308 Vector descriptors = getDescriptors(); 309 if (descriptors == null) { 310 descriptors = initDescriptors(getStatus()); 311 } 312 int size = descriptors.size(); 313 IPropertyDescriptor[] descriptorsArray = new IPropertyDescriptor[size]; 314 descriptorsArray[0] = getInfosDescriptor(getStatus()); 315 descriptorsArray[1] = getWarningsDescriptor(getStatus()); 316 for (int i=2; i<size; i++) { 317 descriptorsArray[i] = (IPropertyDescriptor) descriptors.get(i); 318 } 319 return descriptorsArray; 320 } 321 322 /* (non-Javadoc) 323 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object) 324 */ 325 public Object getPropertyValue(Object propKey) { 326 if (propKey.equals(P_ID_STATUS_INFO)) { 327 if ((getStatus() & INFO_MASK) != 0) { 328 return new Integer(0); 329 } 330 } 331 if (propKey.equals(P_ID_STATUS_WARNING)) { 332 if ((getStatus() & WARNING_MASK) != 0) { 333 return new Integer(0); 334 } 335 } 336 if (propKey.equals(P_ID_STATUS_ERROR)) { 337 if ((getStatus() & BIG_DELTA) != 0) { 338 return "Some builds have tests with regression"; 339 } 340 } 341 if (propKey.equals(P_ID_STATUS_COMMENT)) { 342 IEclipsePreferences preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID); 343 return preferences.get(getId(), ""); 344 } 345 return null; 346 } 347 348 public ResultsElement getResultsElement(String resultName) { 349 int length = getChildren(null).length; 350 for (int i=0; i<length; i++) { 351 ResultsElement searchedResults = this.children[i]; 352 if (searchedResults.getName().equals(resultName)) { 353 return searchedResults; 354 } 355 } 356 return null; 357 } 358 359 /** 360 * Return the status of the element. 361 * 362 * The status is a bit mask pattern where digits are 363 * allowed as follow: 364 * <ul> 365 * <li>0-3: bits for state showing whether the element is 366 * <ul> 367 * <li>{@link #UNKNOWN} : not connected to a db</li> 368 * <li>{@link #UNREAD} : is not valid (e.g. NaN results)</li> 369 * <li>{@link #MISSING} : no results (e.g. the perf machine crashed and didn't store any results)</li> 370 * <li>{@link #READ} : has valid results</li> 371 * </ul> 372 * </li> 373 * <li>4-5: bits for information. Current possible information are 374 * <ul> 375 * <li>{@link #SMALL_VALUE} : build results or delta with baseline value is under 100ms</li> 376 * <li>{@link #STUDENT_TTEST} : the Student T-test is over the threshold (old yellow color for test results)</li> 377 * </ul> 378 * </li> 379 * <li>6-11: bits for warnings. Current possible warnings are 380 * <ul> 381 * <li>{@link #NO_BASELINE} : no baseline for the current build</li> 382 * <li>{@link #SINGLE_RUN} : the test has only one run (i.e. no error could be computed), hence its reliability cannot be evaluated</li> 383 * <li>{@link #BIG_ERROR} : the test result is over the 3% threshold</li> 384 * <li>{@link #NOT_STABLE} : the test history shows a deviation between 10% and 20% (may mean that this test is not so reliable)</li> 385 * <li>{@link #NOT_RELIABLE} : the test history shows a deviation over 20% (surely means that this test is too erratic to be reliable)</li> 386 * </ul> 387 * </li> 388 * <li>12-15: bits for errors. Current possible errors are 389 * <ul> 390 * <li>{@link #BIG_DELTA} : the delta for the test is over the 10% threshold</li> 391 * </ul> 392 * </li> 393 * </ul> 394 * 395 * Note that these explanation applied to {@link BuildResultsElement}, and {@link DimResultsElement}. 396 * For {@link ComponentResultsElement}, and {@link ScenarioResultsElement}, it's the merge of all the children status 397 * and means "Some tests have..." instead of "The test has...". For {@link ConfigResultsElement}, it means the status 398 * of the most recent build compared to its most recent baseline. 399 * 400 * @return An int with each bit set when the corresponding symptom applies. 401 */ 402 public final int getStatus() { 403 if (this.status < 0) { 404 initStatus(); 405 } 406 return this.status; 407 } 408 409 /** 410 * Return the statistics of the build along its history. 411 * 412 * @return An array of double built as follows: 413 * <ul> 414 * <li>0: numbers of values</li> 415 * <li>1: mean of values</li> 416 * <li>2: standard deviation of these values</li> 417 * <li>3: coefficient of variation of these values</li> 418 * </ul> 419 */ 420 double[] getStatistics() { 421 return this.statistics; 422 } 423 424 /** 425 * Returns whether the element (or one in its hierarchy) has an error. 426 * 427 * @return <code> true</code> if the element or one in its hierarchy has an error, 428 * <code> false</code> otherwise 429 */ 430 public final boolean hasError() { 431 return (getStatus() & ERROR_MASK) != 0; 432 } 433 434 void initChildren() { 435 AbstractResults[] resultsChildren = this.results.getChildren(); 436 int length = resultsChildren.length; 437 this.children = new ResultsElement[length]; 438 int count = 0; 439 for (int i=0; i<length; i++) { 440 ResultsElement childElement = createChild(resultsChildren[i]); 441 if (childElement != null) { 442 this.children[count++] = childElement; 443 } 444 } 445 if (count < length) { 446 System.arraycopy(this.children, 0, this.children = new ResultsElement[count], 0, count); 447 } 448 } 449 void initStatus() { 450 this.status = READ; 451 if (this.results != null) { 452 if (this.children == null) initChildren(); 453 int length = this.children.length; 454 for (int i=0; i<length; i++) { 455 this.status |= this.children[i].getStatus(); 456 } 457 } 458 } 459 460 int initStatus(BuildResults buildResults) { 461 this.status = READ; 462 463 // Get values 464 double buildValue = buildResults.getValue(); 465 ConfigResults configResults = (ConfigResults) buildResults.getParent(); 466 BuildResults baselineResults = configResults.getBaselineBuildResults(buildResults.getName()); 467 double baselineValue = baselineResults.getValue(); 468 double delta = (baselineValue - buildValue) / baselineValue; 469 470 // Store if there's no baseline 471 if (Double.isNaN(delta)) { 472 this.status |= NO_BASELINE; 473 } 474 475 // Store if there's only one run 476 long baselineCount = baselineResults.getCount(); 477 long currentCount = buildResults.getCount(); 478 double error = Double.NaN; 479 if (baselineCount == 1 || currentCount == 1) { 480 this.status |= SINGLE_RUN; 481 } 482 483 // Store if the T-test is not good 484 double ttestValue = Util.computeTTest(baselineResults, buildResults); 485 int degreeOfFreedom = (int) (baselineResults.getCount()+buildResults.getCount()-2); 486 if (ttestValue >= 0 && StatisticsUtil.getStudentsT(degreeOfFreedom, StatisticsUtil.T90) >= ttestValue) { 487 this.status |= STUDENT_TTEST; 488 } 489 490 // Store if there's a big error (over 3%) 491 double baselineError = baselineResults.getError(); 492 double currentError = buildResults.getError(); 493 error = Double.isNaN(baselineError) 494 ? currentError / baselineValue 495 : Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue; 496 if (error > 0.03) { 497 this.status |= BIG_ERROR; 498 } 499 500 // Store if there's a big delta (over 10%) 501 if (delta <= -0.1) { 502 this.status |= BIG_DELTA; 503 double currentBuildValue = buildResults.getValue(); 504 double diff = Math.abs(baselineValue - currentBuildValue); 505 if (currentBuildValue < 100 || diff < 100) { // moderate the status when 506 // diff is less than 100ms 507 this.status |= SMALL_VALUE; 508 } else { 509 double[] stats = getStatistics(); 510 if (stats != null) { 511 if (stats[3] > 0.2) { // invalidate the status when the test 512 // historical deviation is over 20% 513 this.status |= NOT_RELIABLE; 514 } else if (stats[3] > 0.1) { // moderate the status when the test 515 // historical deviation is between 10% 516 // and 20% 517 this.status |= NOT_STABLE; 518 } 519 } 520 } 521 } 522 523 return this.status; 524 } 525 526 public boolean isInitialized() { 527 return this.results != null; 528 } 529 530 /* (non-Javadoc) 531 * Method declared on IPropertySource 532 */ 533 public boolean isPropertySet(Object property) { 534 return false; 535 } 536 537 boolean onlyFingerprints() { 538 if (this.parent != null) { 539 return this.parent.onlyFingerprints(); 540 } 541 return ((PerformanceResultsElement)this).fingerprints; 542 } 543 544 /* (non-Javadoc) 545 * Method declared on IPropertySource 546 */ 547 public void resetPropertyValue(Object property) { 548 } 549 550 void resetStatus() { 551 this.status = -1; 552 if (this.results != null) { 553 if (this.children == null) initChildren(); 554 int length = this.children.length; 555 for (int i=0; i<length; i++) { 556 this.children[i].resetStatus(); 557 } 558 } 559 } 560 561 public void setPropertyValue(Object name, Object value) { 562 if (name.equals(P_ID_STATUS_COMMENT)) { 563 IEclipsePreferences preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID); 564 preferences.put(getId(), (String) value); 565 try { 566 preferences.flush(); 567 } catch (BackingStoreException e) { 568 // skip 569 } 570 } 571 } 572 573 /** 574 * Sets the image descriptor 575 */ 576 void setImageDescriptor(ImageDescriptor desc) { 577 // this.imageDescriptor = desc; 578 } 579 580 public String toString() { 581 if (this.results == null) { 582 return getName(); 583 } 584 return this.results.toString(); 585 } 586 587 /* 588 * Write the element status in the given stream 589 */ 590 StringBuffer writableStatus(StringBuffer buffer, int kind, StringBuffer excluded) { 591 int length = this.children.length; 592 for (int i=0; i<length; i++) { 593 this.children[i].writableStatus(buffer, kind, excluded); 594 } 595 return buffer; 596 } 597 598 599 } 600