1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.util.leak;
18
19
20import android.support.test.filters.SmallTest;
21import android.support.test.runner.AndroidJUnit4;
22
23import com.android.systemui.SysuiTestCase;
24import com.android.systemui.util.leak.ReferenceTestUtils.CollectionWaiter;
25
26import org.junit.Before;
27import org.junit.Ignore;
28import org.junit.Test;
29import org.junit.runner.RunWith;
30
31import java.io.FileDescriptor;
32import java.io.FileNotFoundException;
33import java.io.FileOutputStream;
34import java.io.PrintWriter;
35import java.util.ArrayList;
36import java.util.Collection;
37import java.util.function.BiConsumer;
38import java.util.function.Consumer;
39
40@SmallTest
41@RunWith(AndroidJUnit4.class)
42public class LeakDetectorTest extends SysuiTestCase {
43
44    private LeakDetector mLeakDetector;
45
46    // The references for which collection is observed are stored in fields. The allocation and
47    // of these references happens in separate methods (trackObjectWith/trackCollectionWith)
48    // from where they are set to null. The generated code might keep the allocated reference
49    // alive in a dex register when compiling in release mode. As R8 is used to compile this
50    // test the --dontoptimize flag is also required to ensure that these methods are not
51    // inlined, as that would defeat the purpose of having the mutation in methods.
52    private Object mObject;
53    private Collection<?> mCollection;
54
55    private CollectionWaiter trackObjectWith(Consumer<Object> tracker) {
56        mObject = new Object();
57        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(mObject);
58        tracker.accept(mObject);
59        return collectionWaiter;
60    }
61
62    private CollectionWaiter trackCollectionWith(
63            BiConsumer<? super Collection<?>, String> tracker) {
64        mCollection = new ArrayList<>();
65        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(mCollection);
66        tracker.accept(mCollection, "tag");
67        return collectionWaiter;
68    }
69
70    @Before
71    public void setup() {
72        mLeakDetector = LeakDetector.create();
73
74        // Note: Do not try to factor out object / collection waiter creation. The optimizer will
75        // try and cache accesses to fields and thus create a GC root for the duration of the test
76        // method, thus breaking the test.
77    }
78
79    @Test
80    public void trackInstance_doesNotLeakTrackedObject() {
81        CollectionWaiter collectionWaiter = trackObjectWith(mLeakDetector::trackInstance);
82        mObject = null;
83        collectionWaiter.waitForCollection();
84    }
85
86    @Ignore("b/75329085")
87    @Test
88    public void trackCollection_doesNotLeakTrackedObject() {
89        CollectionWaiter collectionWaiter = trackCollectionWith(mLeakDetector::trackCollection);
90        mCollection = null;
91        collectionWaiter.waitForCollection();
92    }
93
94    @Test
95    public void trackGarbage_doesNotLeakTrackedObject() {
96        CollectionWaiter collectionWaiter = trackObjectWith(mLeakDetector::trackGarbage);
97        mObject = null;
98        collectionWaiter.waitForCollection();
99    }
100
101    @Test
102    public void testDump() throws Exception {
103        Object o1 = new Object();
104        Object o2 = new Object();
105        Collection<Object> col1 = new ArrayList<>();
106
107        mLeakDetector.trackInstance(o1);
108        mLeakDetector.trackCollection(col1, "tag");
109        mLeakDetector.trackGarbage(o2);
110
111        FileOutputStream fos = new FileOutputStream("/dev/null");
112        mLeakDetector.dump(fos.getFD(), new PrintWriter(fos), new String[0]);
113    }
114
115    @Test
116    public void testDisabled() throws Exception {
117        mLeakDetector = new LeakDetector(null, null, null);
118
119        Object o1 = new Object();
120        Object o2 = new Object();
121        Collection<Object> col1 = new ArrayList<>();
122
123        mLeakDetector.trackInstance(o1);
124        mLeakDetector.trackCollection(col1, "tag");
125        mLeakDetector.trackGarbage(o2);
126
127        FileOutputStream fos = new FileOutputStream("/dev/null");
128        mLeakDetector.dump(fos.getFD(), new PrintWriter(fos), new String[0]);
129    }
130}
131