• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LeakCheck()35     public LeakCheck() {
36     }
37 
38     @Override
succeeded(Description description)39     protected void succeeded(Description description) {
40         verify();
41     }
42 
getTracker(String tag)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 
verify()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 
LeakInfo()60         LeakInfo() {
61         }
62 
addAllocation(Throwable t)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 
clearAllocations()68         public void clearAllocations() {
69             mThrowables.clear();
70         }
71 
verify()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 
getLeakInfo(Object object)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 
verify()97         void verify() {
98             mObjects.values().forEach(LeakInfo::verify);
99         }
100     }
101 }
102