1340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk/* 2340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * Copyright (C) 2017 The Android Open Source Project 3340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * 4340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * except in compliance with the License. You may obtain a copy of the License at 6340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * 7340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * http://www.apache.org/licenses/LICENSE-2.0 8340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * 9340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * Unless required by applicable law or agreed to in writing, software distributed under the 10340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * KIND, either express or implied. See the License for the specific language governing 12340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk * permissions and limitations under the License. 13340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk */ 14340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 15340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkpackage android.testing; 16340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 17340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport android.util.ArrayMap; 18340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport android.util.Log; 19340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 20340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport org.junit.Assert; 21340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport org.junit.rules.TestWatcher; 22340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport org.junit.runner.Description; 23340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 24340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport java.io.PrintWriter; 25340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport java.io.StringWriter; 26340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport java.util.ArrayList; 27340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport java.util.HashMap; 28340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport java.util.List; 29340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkimport java.util.Map; 30340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 31340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkpublic class LeakCheck extends TestWatcher { 32340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 33340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk private final Map<String, Tracker> mTrackers = new HashMap<>(); 34340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 35340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public LeakCheck() { 36340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 37340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 38340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk @Override 39340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk protected void succeeded(Description description) { 40340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk verify(); 41340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 42340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 43340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public Tracker getTracker(String tag) { 44340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk Tracker t = mTrackers.get(tag); 45340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk if (t == null) { 46340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk t = new Tracker(); 47340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk mTrackers.put(tag, t); 48340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 49340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk return t; 50340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 51340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 52340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public void verify() { 53340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk mTrackers.values().forEach(Tracker::verify); 54340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 55340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 56340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public static class LeakInfo { 57340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk private static final String TAG = "LeakInfo"; 58340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk private List<Throwable> mThrowables = new ArrayList<>(); 59340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 60340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk LeakInfo() { 61340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 62340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 63340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public void addAllocation(Throwable t) { 64340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk // TODO: Drop off the first element in the stack trace here to have a cleaner stack. 65340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk mThrowables.add(t); 66340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 67340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 68340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public void clearAllocations() { 69340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk mThrowables.clear(); 70340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 71340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 72340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk void verify() { 73340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk if (mThrowables.size() == 0) return; 74340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk Log.e(TAG, "Listener or binding not properly released"); 75340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk for (Throwable t : mThrowables) { 76340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk Log.e(TAG, "Allocation found", t); 77340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 78340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk StringWriter writer = new StringWriter(); 79340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk mThrowables.get(0).printStackTrace(new PrintWriter(writer)); 80340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk Assert.fail("Listener or binding not properly released\n" 81340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk + writer.toString()); 82340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 83340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 84340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 85340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public static class Tracker { 86340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk private Map<Object, LeakInfo> mObjects = new ArrayMap<>(); 87340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 88340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk public LeakInfo getLeakInfo(Object object) { 89340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk LeakInfo leakInfo = mObjects.get(object); 90340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk if (leakInfo == null) { 91340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk leakInfo = new LeakInfo(); 92340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk mObjects.put(object, leakInfo); 93340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 94340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk return leakInfo; 95340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 96340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk 97340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk void verify() { 98340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk mObjects.values().forEach(LeakInfo::verify); 99340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 100340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk } 101340b0e5216b4fcc435e0459b1ca46155a572100dJason Monk} 102