blob: 8daaa8f166640a1bad5179913b025dc3f53af842 [file] [log] [blame]
Jason Monk340b0e52017-03-08 14:57:56 -05001/*
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
15package android.testing;
16
17import android.util.ArrayMap;
18import android.util.Log;
19
20import org.junit.Assert;
21import org.junit.rules.TestWatcher;
22import org.junit.runner.Description;
23
24import java.io.PrintWriter;
25import java.io.StringWriter;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.List;
29import java.util.Map;
30
31public 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}