Home | History | Annotate | Download | only in testing
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
      5  * except in compliance with the License. You may obtain a copy of the License at
      6  *
      7  *      http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the
     10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     11  * KIND, either express or implied. See the License for the specific language governing
     12  * permissions and limitations under the License.
     13  */
     14 
     15 package android.testing;
     16 
     17 import android.util.ArrayMap;
     18 import android.util.Log;
     19 
     20 import org.junit.Assert;
     21 import org.junit.rules.TestWatcher;
     22 import org.junit.runner.Description;
     23 
     24 import java.io.PrintWriter;
     25 import java.io.StringWriter;
     26 import java.util.ArrayList;
     27 import java.util.HashMap;
     28 import java.util.List;
     29 import java.util.Map;
     30 
     31 public class LeakCheck extends TestWatcher {
     32 
     33     private final Map<String, Tracker> mTrackers = new HashMap<>();
     34 
     35     public LeakCheck() {
     36     }
     37 
     38     @Override
     39     protected void succeeded(Description description) {
     40         verify();
     41     }
     42 
     43     public Tracker getTracker(String tag) {
     44         Tracker t = mTrackers.get(tag);
     45         if (t == null) {
     46             t = new Tracker();
     47             mTrackers.put(tag, t);
     48         }
     49         return t;
     50     }
     51 
     52     public void verify() {
     53         mTrackers.values().forEach(Tracker::verify);
     54     }
     55 
     56     public static class LeakInfo {
     57         private static final String TAG = "LeakInfo";
     58         private List<Throwable> mThrowables = new ArrayList<>();
     59 
     60         LeakInfo() {
     61         }
     62 
     63         public void addAllocation(Throwable t) {
     64             // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
     65             mThrowables.add(t);
     66         }
     67 
     68         public void clearAllocations() {
     69             mThrowables.clear();
     70         }
     71 
     72         void verify() {
     73             if (mThrowables.size() == 0) return;
     74             Log.e(TAG, "Listener or binding not properly released");
     75             for (Throwable t : mThrowables) {
     76                 Log.e(TAG, "Allocation found", t);
     77             }
     78             StringWriter writer = new StringWriter();
     79             mThrowables.get(0).printStackTrace(new PrintWriter(writer));
     80             Assert.fail("Listener or binding not properly released\n"
     81                     + writer.toString());
     82         }
     83     }
     84 
     85     public static class Tracker {
     86         private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
     87 
     88         public LeakInfo getLeakInfo(Object object) {
     89             LeakInfo leakInfo = mObjects.get(object);
     90             if (leakInfo == null) {
     91                 leakInfo = new LeakInfo();
     92                 mObjects.put(object, leakInfo);
     93             }
     94             return leakInfo;
     95         }
     96 
     97         void verify() {
     98             mObjects.values().forEach(LeakInfo::verify);
     99         }
    100     }
    101 }
    102