PageRenderTime 237ms CodeModel.GetById 168ms app.highlight 63ms RepoModel.GetById 1ms app.codeStats 1ms

/testability-explorer/src/test/java/com/google/test/metric/MetricComputerTest.java

http://testability-explorer.googlecode.com/
Java | 591 lines | 436 code | 102 blank | 53 comment | 1 complexity | 79a6ab29bcd912049e1e80f794dfc9a7 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.test.metric.report.DrillDownReportGenerator;
 19import com.google.test.metric.testing.MetricComputerBuilder;
 20import com.google.test.metric.testing.MetricComputerJavaDecorator;
 21
 22import java.io.ByteArrayOutputStream;
 23import java.io.PrintStream;
 24import java.util.List;
 25
 26import javax.swing.JLabel;
 27
 28public class MetricComputerTest extends AutoFieldClearTestCase {
 29
 30  private MetricComputerJavaDecorator computer;
 31  private final ClassRepository repo = new JavaClassRepository();
 32
 33  @Override
 34  protected void setUp() throws Exception {
 35    super.setUp();
 36    RegExpWhiteList regExpWhitelist = new RegExpWhiteList("java.");
 37    regExpWhitelist.addPackage("javax.");
 38    MetricComputer toDecorate = new MetricComputerBuilder().withWhitelist(regExpWhitelist)
 39        .withClassRepository(repo).build();
 40    computer = new MetricComputerJavaDecorator(toDecorate, repo);
 41  }
 42
 43  public static class Medium {
 44    public Medium() {
 45      statiCost1();
 46      cost2();
 47    }
 48
 49    /**
 50     * I cost 1
 51     */
 52    public static int statiCost1() {
 53      int i = 0;
 54      return i > 0 ? 1 : 2;
 55    }
 56
 57    /**
 58     * I cost 2, but I am instance method so I can be overridden. so my cost may
 59     * be avoided in most cases.
 60     */
 61    public int cost2() {
 62      int i = 0;
 63      return i > 0 ? i > 1 ? 1 : 2 : 2;
 64    }
 65
 66    /**
 67     * I cost 2, but I am a <em>final</em> instance method that can not be overridden.
 68     * My cost is unavoidable.
 69     */
 70    public final int finalCost2() {
 71      int i = 0;
 72      return i > 0 ? i > 1 ? 1 : 2 : 2;
 73    }
 74
 75    /**
 76     * I am instance method hence you will have to add the cost of constructor
 77     * to me. (by myself I cost 4)
 78     */
 79    public Object testMethod4() {
 80      int i = 0;
 81      i = i > 0 ? 1 : 2;
 82      i = i > 0 ? 1 : 2;
 83      i = i > 0 ? 1 : 2;
 84      i = i > 0 ? 1 : 2;
 85      return new Object();
 86    }
 87  }
 88
 89  public void testMediumCost1() throws Exception {
 90    MethodInfo method = repo.getClass(Medium.class.getCanonicalName()).getMethod("int statiCost1()");
 91    assertFalse(method.canOverride());
 92    MethodCost cost = computer.compute(Medium.class, "int statiCost1()");
 93    assertEquals(1l, cost.getTotalCost().getCyclomaticComplexityCost());
 94  }
 95
 96  /**
 97   * Since cost2 is called twice, once by our test and once by constructor we
 98   * don't want to add it twice. The constructor adds 1. The direct method call
 99   * adds 2. So the total cost is 3.
100   */
101  public void testMediumCost2() throws Exception {
102    MethodInfo method = repo.getClass(Medium.class.getCanonicalName()).getMethod("int cost2()");
103    assertTrue(method.canOverride());
104    MethodCost cost = computer.compute(Medium.class, "int cost2()");
105    assertEquals(3l, cost.getTotalCost().getCyclomaticComplexityCost());
106  }
107
108  public void testMediumFinalCost2() throws Exception {
109    MethodInfo method = repo.getClass(Medium.class.getCanonicalName()).getMethod("int finalCost2()");
110    assertFalse(method.canOverride());
111  }
112
113  /**
114   * Cost of the constructor needs to add only the cost of the static method it calls.
115   * (The static method can not be overridden). The cost of the instance method can be
116   * overridden in a subclass.
117   */
118  public void testMediumInit() throws Exception {
119    MethodInfo method = repo.getClass(Medium.class.getCanonicalName()).getMethod("Medium()");
120    assertFalse(method.canOverride());
121    MethodCost cost = computer.compute(Medium.class, "Medium()");
122    assertEquals(1l, cost.getTotalCost().getCyclomaticComplexityCost());
123  }
124
125  /**
126   * Method4 is cost of 4 by itself, but one has to add the cost of constructor
127   * since it is an instance method. The constructor is 0 but calls two methods:
128   * cost1 method is static and can not be intercepted hence it has to be added.
129   * cost2 method is instance and can be overridden hence we don't add that
130   * cost.
131   */
132  public void testMediumMethod4() throws Exception {
133    MethodCost cost = computer.compute(Medium.class,
134        "java.lang.Object testMethod4()");
135    assertEquals(5l, cost.getTotalCost().getCyclomaticComplexityCost());
136  }
137
138  public static class Node {
139    public String cost1() {
140      int a = 0;
141      return a == 2 ? "" : null;
142    }
143  }
144
145  public static class TreeInjection {
146    private Node subTitle; // non-injectable
147    public Node title = new Node(); // injectable b/c public field (only after constructor)
148    private final Node footnote; // injectable via constructor
149
150    public TreeInjection(Node footnote) {
151      this.footnote = footnote;
152    }
153
154    public String titleTcc0() {
155      return title.cost1();
156    }
157
158    public String subTitleTcc1() {
159      return subTitle.cost1();
160    }
161
162    public String footnoteTcc0() {
163      return footnote.cost1();
164    }
165
166    public String veryExpensive() {
167      return "".toLowerCase();
168    }
169  }
170
171  public void testTreeConstructorHasZeroCost() throws Exception {
172    MethodCost cost = computer.compute(TreeInjection.class,
173        "TreeInjection(com.google.test.metric.MetricComputerTest.Node)");
174    assertEquals(0l, cost.getTotalCost().getCyclomaticComplexityCost());
175    assertEquals(0l, cost.getTotalCost().getGlobalCost());
176  }
177
178  public void testTreeTitleTcc0CostIsZeroBecauseInjectable() throws Exception {
179    MethodCost cost = computer.compute(TreeInjection.class,
180        "java.lang.String titleTcc0()");
181    assertEquals(0l, cost.getTotalCost().getCyclomaticComplexityCost());
182  }
183
184  public void testTreeSubTitleTcc1CostIsOneBecauseNonInjectable() throws Exception {
185    MethodCost cost = computer.compute(TreeInjection.class,
186        "java.lang.String subTitleTcc1()");
187    assertEquals(1l, cost.getTotalCost().getCyclomaticComplexityCost());
188  }
189
190  public void testTreeFootnoteTcc0CostIsZeroBecauseInjectable() throws Exception {
191    MethodCost cost = computer.compute(TreeInjection.class,
192        "java.lang.String footnoteTcc0()");
193    assertEquals(0l, cost.getTotalCost().getCyclomaticComplexityCost());
194  }
195
196  static class ChoseConstructor {
197    ChoseConstructor() {
198    }
199
200    ChoseConstructor(Object a) {
201    }
202
203    ChoseConstructor(Object a, int c, int d) {
204    }
205
206    ChoseConstructor(Object a, Object b) {
207    }
208  }
209
210  public void testChooseConstructorWithMostNonPrimitiveParameters() throws Exception {
211    ClassInfo classInfo = repo.getClass(ChoseConstructor.class.getCanonicalName());
212    MethodInfo constructor = classInfo.getConstructorWithMostNonPrimitiveParameters();
213    assertEquals("ChoseConstructor(java.lang.Object, java.lang.Object)", constructor.getName());
214  }
215
216  static class Singleton {
217    private Singleton() {
218      CostUtil.staticCost1();
219    }
220
221    public void doWork() {
222      CostUtil.staticCost2();
223    }
224  }
225
226  public void testIgnoreConstructorsIfAllConstructorsArePrivate() throws Exception {
227    assertEquals(2L, computer.compute(Singleton.class, "void doWork()").getTotalCost().getCyclomaticComplexityCost());
228    ClassInfo classInfo = repo.getClass(Singleton.class.getCanonicalName());
229    MethodInfo constructor = classInfo
230        .getConstructorWithMostNonPrimitiveParameters();
231    assertNull("Constructor should not be found when private",  constructor);
232  }
233
234  static class StaticInit {
235    static {
236      CostUtil.staticCost1();
237    }
238
239    public void doWork() {
240      CostUtil.staticCost2();
241    }
242  }
243
244  public void testAddStaticInitializationCost() throws Exception {
245    assertEquals(3L, computer.compute(StaticInit.class, "void doWork()").getTotalCost().getCyclomaticComplexityCost());
246  }
247
248  static class Setters {
249    private Object o;
250
251    public void setO(Object o) {
252      this.o = o;
253    }
254
255    public void doWork() {
256      o.toString();
257    }
258  }
259
260  public void testSetterInjection() throws Exception {
261    MethodCost cost = computer.compute(Setters.class, "void doWork()");
262    assertEquals(0L, cost.getTotalCost().getCyclomaticComplexityCost());
263  }
264
265  static class WholeClassCost {
266    void methodA() {
267      CostUtil.staticCost1();
268    }
269
270    void methodB() {
271      CostUtil.staticCost1();
272    }
273  }
274
275  public void testComputeClassCost() throws Exception {
276    ClassCost cost = computer.compute(WholeClassCost.class);
277    assertEquals(1L, cost.getMethodCost("void methodA()").getTotalCost().getCyclomaticComplexityCost());
278    assertEquals(1L, cost.getMethodCost("void methodB()").getTotalCost().getCyclomaticComplexityCost());
279  }
280
281  static class Array {
282    String[] strings;
283
284    public void method() {
285      strings.clone();
286    }
287  }
288
289  public void testArray() throws Exception {
290    repo.getClass(String[].class.getName());
291    computer.compute(repo.getClass(Array.class.getCanonicalName()).getMethod("void method()"));
292  }
293
294  static class InjectableClass {
295    public void cost4() {
296      CostUtil.staticCost4();
297    }
298
299    public static void callCost0(InjectableClass ref) {
300      indirection(ref);
301    }
302
303    private static void indirection(InjectableClass ref) {
304      ref.cost4();
305    }
306    public static void callCost4() {
307      InjectableClass x = new InjectableClass();
308      indirection(x);
309    }
310  }
311
312  public void testInjectabilityIsTransitive() throws Exception {
313    MethodCost callCost0 = computer.compute(InjectableClass.class, "void callCost0("
314        + "com.google.test.metric.MetricComputerTest.InjectableClass)");
315    assertEquals(0L, callCost0.getTotalCost().getCyclomaticComplexityCost());
316
317    MethodCost callCost4 = computer.compute(InjectableClass.class, "void callCost4()");
318    assertEquals(4L, callCost4.getTotalCost().getCyclomaticComplexityCost());
319  }
320
321  static class GlobalState {
322    int i;
323    static final String X = "X";
324
325    public int inc() {
326      return i++;
327    }
328
329    public void noop() {
330    }
331
332    @Override
333    public String toString() {
334      return X;
335    }
336  }
337
338  static final GlobalState ref = new GlobalState();
339  static int count;
340  static class GlobalStateUser {
341
342    // StateLoad: 0
343    public void noLoad() {
344    }
345
346    // StateLoad: 1
347    public void accessCount() {
348      count++;
349    }
350
351    // StateLoad: 0
352    public void accessFinalState() {
353      ref.noop();
354    }
355
356    // StateLoad: 0
357    public void accessFinalState2() {
358      ref.toString();
359    }
360
361    // StateLoad: 1
362    public void accessMutableState() {
363      ref.inc();
364    }
365  }
366
367  public void testGlobalLoadWhichAccessesFinalShouldBeZero() {
368    ClassCost cost = computer.compute(GlobalState.class);
369    MethodCost method = cost.getMethodCost("java.lang.String toString()");
370    assertEquals(0L, method.getTotalCost().getGlobalCost());
371  }
372
373  public void testGlobalLoadMethodDispatchNoStateAccessShouldBeZero() {
374    ClassCost cost = computer.compute(GlobalStateUser.class);
375    assertEquals(0L, cost.getMethodCost("void noLoad()").getTotalCost().getGlobalCost());
376    assertEquals(0L, cost.getMethodCost("void accessFinalState()").getTotalCost().getGlobalCost());
377    assertEquals(0L, cost.getMethodCost("void accessFinalState2()").getTotalCost().getGlobalCost());
378  }
379
380  public void testGlobalLoadAccessStateShouldBeOne() {
381    MethodCost cost = computer.compute(GlobalStateUser.class, "void accessCount()");
382    assertEquals(1L, cost.getTotalCost().getGlobalCost());
383  }
384
385  public void testGlobalLoadAccessStateThroughFinalShouldBeOne() {
386    MethodCost cost =
387        computer.compute(GlobalStateUser.class, "void accessMutableState()");
388    new DrillDownReportGenerator(new PrintStream(new ByteArrayOutputStream()), new CostModel(),
389        null, Integer.MAX_VALUE, 0).print("", cost, 10);
390    assertEquals("Expecting one for read and one for write", 2L,
391        cost.getTotalCost().getGlobalCost());
392  }
393
394  public void testJavaLangObjectParsesCorrectly() throws Exception {
395    repo.getClass(Object.class.getCanonicalName());
396  }
397
398  public static class CostPerLine {
399    static void main(){
400      CostUtil.staticCost0(); // line0
401      CostUtil.staticCost1(); // line1
402      CostUtil.staticCost2(); // line2
403    }
404  }
405
406  public void testCostPerLine() throws Exception {
407    MethodCost cost = computer.compute(CostPerLine.class, "void main()");
408    assertEquals(3, cost.getTotalCost().getCyclomaticComplexityCost());
409    List<? extends ViolationCost> lineNumberCosts = cost.getViolationCosts();
410    assertEquals(3, lineNumberCosts.size());
411
412    MethodInvocationCost line0 = (MethodInvocationCost) lineNumberCosts.get(0);
413    MethodInvocationCost line1 = (MethodInvocationCost) lineNumberCosts.get(1);
414    MethodInvocationCost line2 = (MethodInvocationCost) lineNumberCosts.get(2);
415
416    int methodStartingLine = cost.getMethodLineNumber();
417
418    assertEquals(0, line0.getMethodCost().getTotalCost().getCyclomaticComplexityCost());
419    assertEquals(methodStartingLine + 0, line0.getLocation().getLineNumber());
420
421    assertEquals(1, line1.getMethodCost().getTotalCost().getCyclomaticComplexityCost());
422    assertEquals(methodStartingLine + 1, line1.getLocation().getLineNumber());
423
424    assertEquals(2, line2.getMethodCost().getTotalCost().getCyclomaticComplexityCost());
425    assertEquals(methodStartingLine + 2, line2.getLocation().getLineNumber());
426  }
427
428  public static class WhiteListTest {
429    public void testMethod() {
430      new String(new byte[0]);
431    }
432  }
433
434  public void testWhiteList() throws Exception {
435    RegExpWhiteList customWhitelist = new RegExpWhiteList("java.lang");
436    MetricComputer toDecorate = new MetricComputerBuilder().withClassRepository(repo)
437        .withWhitelist(customWhitelist).build();
438    computer = new MetricComputerJavaDecorator(toDecorate, repo);
439
440    MethodCost cost = computer.compute(WhiteListTest.class, "void testMethod()");
441    assertEquals(0L, cost.getTotalCost().getGlobalCost());
442  }
443
444  static class DoubleCountClassConst {
445    static int x;
446    static {
447      x = 1;
448    }
449  }
450  public void testDoubleCountClassConst() throws Exception {
451    ClassCost cost = computer.compute(DoubleCountClassConst.class);
452    assertEquals(1, cost.getMethodCost("DoubleCountClassConst()").getTotalCost().getGlobalCost());
453  }
454
455  static enum TestEnum1{ ONE }
456  public void testEnumerationIsZero() throws Exception {
457    RegExpWhiteList customWhitelist = new RegExpWhiteList("java.");
458    MetricComputer toDecorate = new MetricComputerBuilder().withClassRepository(repo)
459        .withWhitelist(customWhitelist).build();
460    computer = new MetricComputerJavaDecorator(toDecorate, repo);
461    ClassCost cost = computer.compute(TestEnum1.class);
462    assertEquals(0, cost.getMethodCost("<static init>()").getTotalCost().getGlobalCost());
463  }
464
465  public void testSyntheticEnumValuesAccessorClassIsZero() throws Exception {
466    RegExpWhiteList customWhitelist = new RegExpWhiteList("java.");
467    MetricComputer toDecorate = new MetricComputerBuilder().withClassRepository(repo)
468        .withWhitelist(customWhitelist).build();
469    computer = new MetricComputerJavaDecorator(toDecorate, repo);
470    ClassCost cost;
471    try {
472      cost = computer.compute("com.google.test.metric.InnerEnumHolder.1");
473    } catch (ClassNotFoundException e) {
474      // Eclipse names its enumerations differently
475      cost = computer.compute("com.google.test.metric.InnerEnumHolder.NodeType");
476    }
477    assertEquals(0, cost.getMethodCost("<static init>()").getTotalCost().getGlobalCost());
478  }
479
480  private final String testValue = null;
481  class InnerClassTest {
482    public void test() {
483      testValue.indexOf(0);
484    }
485  }
486  public void XtestInnerClassInjectability() throws Exception {
487    MethodCost cost = computer.compute(InnerClassTest.class, "void test()");
488    assertEquals(0, cost.getTotalCost().getCyclomaticComplexityCost());
489  }
490
491  private static class ScoreTooHigh {
492    public void doMockery(Builder builder) {
493      Product product = builder.build();
494      product.execute();
495    }
496    public static class Product {
497      public void execute() {
498        CostUtil.staticCost2();
499      }
500    }
501    public static class Builder {
502      public Product build() {
503        CostUtil.staticCost4();
504        return null;
505      }
506    }
507  }
508
509  public void testReturnValueFromInjectableIsInjectable() throws Exception {
510    MethodCost cost = computer.compute(ScoreTooHigh.class,
511        "void doMockery(com.google.test.metric.MetricComputerTest.ScoreTooHigh.Builder)");
512    assertEquals(0, cost.getTotalCost().getCyclomaticComplexityCost());
513  }
514
515  public static class NonDeterministicCostUtil {
516
517    public static int global;
518    public int notGlobal;
519    public int value;
520
521    private int trinary(boolean bool) {
522        try {
523          return !bool ? notGlobal : global;
524        } catch (Exception e) {
525          return 1;
526        }
527    }
528
529    public void test() {
530      value = trinary(false);
531    }
532
533
534   }
535
536  public void testWhenLeftAndRightSideOfTrinaryReturnDifferentCostUseHigherOne()
537      throws Exception {
538    MethodCost cost = computer.compute(NonDeterministicCostUtil.class,
539        "void test()");
540    assertEquals(1, cost.getTotalCost().getGlobalCost());
541  }
542
543  public static class HasIrrelevantImplicitCost {
544    public HasIrrelevantImplicitCost() {
545      CostUtil.staticCost2();
546    }
547    public void setNoCost(int i) {
548
549    }
550    public void setWithCost(int i) {
551      CostUtil.staticCost1();
552    }
553    public void execute() {
554
555    }
556  }
557
558  public void testZeroImplicitCostNotCounted() throws Exception {
559    MethodCost cost = computer.compute(HasIrrelevantImplicitCost.class, "void execute()");
560    List<? extends ViolationCost> implicitViolationCosts = cost.getImplicitViolationCosts();
561
562    assertEquals(2, implicitViolationCosts.size());
563    MethodInvocationCost constructor = (MethodInvocationCost) implicitViolationCosts.get(0);
564
565    assertEquals("implicit cost from construction", constructor.getReason());
566    assertEquals(2, constructor.getCost().getCyclomaticComplexityCost());
567    assertTrue(constructor.getMethodCost().getImplicitViolationCosts().isEmpty());
568
569    MethodInvocationCost setter = (MethodInvocationCost) implicitViolationCosts.get(1);
570    assertEquals("implicit cost calling all setters", setter.getReason());
571    assertEquals("void setWithCost(int)", setter.getMethodCost().getMethodName());
572
573  }
574
575  public void testConstructorDoesntCountSetters() throws Exception {
576    MethodCost cost = computer.compute(HasIrrelevantImplicitCost.class,
577      "HasIrrelevantImplicitCost()");
578    assertEquals(0, cost.getImplicitViolationCosts().size());
579  }
580
581  static class MyLabel extends JLabel {
582    void doThing() {
583    }
584  }
585
586  public void testJavaXSuperClassSettersArentCountedAgainstMe() throws Exception {
587    ClassCost cost = computer.compute(MyLabel.class);
588    assertEquals(0, cost.getTotalComplexityCost());
589    assertEquals(0, cost.getTotalGlobalCost());
590  }
591}