PageRenderTime 27ms CodeModel.GetById 15ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/testability-explorer/src/main/java/com/google/test/metric/MetricComputer.java

http://testability-explorer.googlecode.com/
Java | 136 lines | 76 code | 15 blank | 45 comment | 11 complexity | 4b08889e35dbe031e5978dab63902f76 MD5 | raw file
  1/*
  2 * Copyright 2007 Google Inc.
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5 * use this file except in compliance with the License. You may obtain a copy of
  6 * the License at
  7 *
  8 * http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 13 * License for the specific language governing permissions and limitations under
 14 * the License.
 15 */
 16package com.google.test.metric;
 17
 18import com.google.inject.Inject;
 19import com.google.inject.name.Named;
 20import com.google.test.metric.ConfigModule.Error;
 21import static com.google.test.metric.Reason.IMPLICIT_CONSTRUCTOR;
 22import static com.google.test.metric.Reason.IMPLICIT_SETTER;
 23import static com.google.test.metric.Reason.IMPLICIT_STATIC_INIT;
 24import com.google.test.metric.TestabilityVisitor.CostRecordingFrame;
 25import com.google.test.metric.TestabilityVisitor.Frame;
 26
 27import java.io.PrintStream;
 28import java.util.ArrayList;
 29import java.util.List;
 30
 31public class MetricComputer {
 32
 33  private final ClassRepository classRepository;
 34  private final PrintStream err;
 35  private final WhiteList whitelist;
 36  private final int recordingDepth;
 37
 38  @Inject
 39  public MetricComputer(ClassRepository classRepository, @Error PrintStream err,
 40      WhiteList whitelist, @Named("printDepth") int recordingDepth) {
 41    this.classRepository = classRepository;
 42    this.err = err;
 43    this.whitelist = whitelist;
 44    this.recordingDepth = recordingDepth;
 45  }
 46
 47  public ClassCost compute(String name) {
 48    return compute(classRepository.getClass(name));
 49  }
 50
 51  /**
 52   * Computing the ClassCost for a ClassInfo involves tallying up all the MethodCosts contained
 53   * in the class. Then an overall cost is calculated, based on the {@code CostModel} the metric
 54   * computer is using.
 55   *
 56   * @param clazz to compute the metric for.
 57   * @return classCost
 58   */
 59  public ClassCost compute(ClassInfo clazz) {
 60    List<MethodCost> methods = new ArrayList<MethodCost>();
 61    for (MethodInfo method : clazz.getMethods()) {
 62      methods.add(compute(method));
 63    }
 64    return new ClassCost(clazz.getName(), methods);
 65  }
 66
 67  /**
 68   * Computing the MethodCost for a MethodInfo involves tallying up:
 69   * <ul><li>The cost in any static initialization blocks of the class which holds the method.</li>
 70   * <li>The cost of constructing the object.</li>
 71   * <li>Recognizing injectability through setter methods. This in most cases improves the score
 72   * unless you have lots of code in your setter.</li>
 73   * <li>The field costs</li>
 74   * <li>Lastly the costs are added up for all of the lines in this method. This includes the
 75   * transitive non-mockable/interceptable closure of all the costs of the methods that are called.</li>
 76   * <li></li></ul>
 77   *
 78   * @param method to compute the cost for.
 79   * @return MethodCost for this method, including the accumulated costs the methods it calls. This
 80   * MethodCost is guaranteed to have already been linked (sealed for adding additional costs).
 81   */
 82  public MethodCost compute(MethodInfo method) {
 83    TestabilityVisitor visitor = new TestabilityVisitor(classRepository, new VariableState(), err, whitelist);
 84    TestabilityVisitor.CostRecordingFrame frame = visitor.createFrame(method, recordingDepth);
 85    addStaticInitializationCost(method, frame);
 86    if (!method.isStatic() && !method.isConstructor()) {
 87      addConstructorCost(method, frame);
 88      addSetterInjection(method, frame);
 89    }
 90    addFieldCost(method, frame);
 91    return frame.applyMethodOperations();
 92  }
 93
 94
 95
 96  /** Goes through all methods and adds an implicit cost for those beginning with "set" (assuming
 97   * to test the {@code baseMethod}'s class, you need to be able to call the setters for initialization.  */
 98  private void addSetterInjection(MethodInfo baseMethod, CostRecordingFrame frame) {
 99    for (MethodInfo setter : baseMethod.getSiblingSetters()) {
100      frame.applyImplicitCost(setter, IMPLICIT_SETTER);
101    }
102  }
103
104  /** Adds an implicit cost to all non-static methods for calling the constructor. (Because to test
105   * any instance method, you must be able to instantiate the class.) Also marks parameters
106   * injectable for the constructor with the most non-primitive parameters. */
107  private void addConstructorCost(MethodInfo method, CostRecordingFrame frame) {
108    MethodInfo constructor = method.getClassInfo().getConstructorWithMostNonPrimitiveParameters();
109    if (constructor != null) {
110      frame.applyImplicitCost(constructor, IMPLICIT_CONSTRUCTOR);
111    }
112  }
113
114  /** Doesn't really add the field costs (there are none), but marks non-private fields as injectable. */
115  private void addFieldCost(MethodInfo method,
116      Frame frame) {
117    for (FieldInfo field : method.getClassInfo().getFields()) {
118      if (!field.isPrivate()) {
119        frame.getGlobalVariables().setInjectable(field);
120      }
121    }
122  }
123
124   /** Includes the cost of all static initialization blocks, as well as static field assignments. */
125  private void addStaticInitializationCost(MethodInfo baseMethod, CostRecordingFrame frame) {
126    if (baseMethod.isStaticConstructor()) {
127      return;
128    }
129    for (MethodInfo method : baseMethod.getClassInfo().getMethods()) {
130      if (method.isStaticConstructor()) {
131        frame.applyImplicitCost(method, IMPLICIT_STATIC_INIT);
132      }
133    }
134  }
135
136}