1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *      http://www.apache.org/licenses/LICENSE-2.0
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License.
12 */
13
14package android.databinding.testapp;
15
16import android.databinding.testapp.databinding.LeakTestBinding;
17import android.test.ActivityInstrumentationTestCase2;
18import android.util.Log;
19import android.widget.FrameLayout;
20
21import java.lang.ref.WeakReference;
22import java.util.ArrayList;
23
24public class LeakTest extends ActivityInstrumentationTestCase2<TestActivity> {
25    WeakReference<LeakTestBinding> mWeakReference = new WeakReference<LeakTestBinding>(null);
26
27    public LeakTest() {
28        super(TestActivity.class);
29    }
30
31    @Override
32    protected void setUp() throws Exception {
33        super.setUp();
34
35        try {
36            getActivity().runOnUiThread(new Runnable() {
37                @Override
38                public void run() {
39                    try {
40                        LeakTestBinding binding = LeakTestBinding.inflate(
41                                getActivity().getLayoutInflater());
42                        getActivity().setContentView(binding.getRoot());
43                        mWeakReference = new WeakReference<LeakTestBinding>(binding);
44                        binding.setName("hello world");
45                        binding.executePendingBindings();
46                    } catch (Exception e) {
47                        e.printStackTrace();
48                        throw e;
49                    }
50                }
51            });
52            getInstrumentation().waitForIdleSync();
53        } catch (Throwable t) {
54            throw new Exception(t);
55        }
56    }
57
58    public void testBindingLeak() throws Throwable {
59        assertNotNull(mWeakReference.get());
60        runTestOnUiThread(new Runnable() {
61            @Override
62            public void run() {
63                getActivity().setContentView(new FrameLayout(getActivity()));
64            }
65        });
66        WeakReference<Object> canary = new WeakReference<Object>(new Object());
67        ArrayList<WeakReference<byte[]>> leak = new ArrayList<>();
68        while (canary.get() != null) {
69            leak.add(new WeakReference<byte[]>(new byte[100]));
70            System.gc();
71        }
72        assertNull(mWeakReference.get());
73    }
74
75    // Test to ensure that when the View is detached that it doesn't rebind
76    // the dirty Views. The rebind should happen only after the root view is
77    // reattached.
78    public void testNoChangeWhenDetached() throws Throwable {
79        final LeakTestBinding binding = mWeakReference.get();
80        final AnimationWatcher watcher = new AnimationWatcher();
81
82        runTestOnUiThread(new Runnable() {
83            @Override
84            public void run() {
85                getActivity().setContentView(new FrameLayout(getActivity()));
86                binding.setName("goodbye world");
87                getActivity().getWindow().getDecorView().postOnAnimation(watcher);
88            }
89        });
90
91        watcher.waitForAnimationThread();
92
93        runTestOnUiThread(new Runnable() {
94            @Override
95            public void run() {
96                assertEquals("hello world", binding.textView.getText().toString());
97                getActivity().setContentView(binding.getRoot());
98                getActivity().getWindow().getDecorView().postOnAnimation(watcher);
99            }
100        });
101
102        watcher.waitForAnimationThread();
103
104        runTestOnUiThread(new Runnable() {
105            @Override
106            public void run() {
107                assertEquals("goodbye world", binding.textView.getText().toString());
108            }
109        });
110    }
111
112    private static class AnimationWatcher implements Runnable {
113        private boolean mWaiting = true;
114
115        public void waitForAnimationThread() throws InterruptedException {
116            synchronized (this) {
117                while (mWaiting) {
118                    this.wait();
119                }
120                mWaiting = true;
121            }
122        }
123
124
125        @Override
126        public void run() {
127            synchronized (this) {
128                mWaiting = false;
129                this.notifyAll();
130            }
131        }
132    }
133}
134