/*
 * Copyright (C) 2015 The Android Open Source Project
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.databinding.testapp;

import android.databinding.testapp.databinding.LeakTestBinding;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.widget.FrameLayout;

import java.lang.ref.WeakReference;

public class LeakTest extends ActivityInstrumentationTestCase2<TestActivity> {
    WeakReference<LeakTestBinding> mWeakReference = new WeakReference<LeakTestBinding>(null);

    public LeakTest() {
        super(TestActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();

        try {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        LeakTestBinding binding = LeakTestBinding.inflate(
                                getActivity().getLayoutInflater());
                        getActivity().setContentView(binding.getRoot());
                        mWeakReference = new WeakReference<LeakTestBinding>(binding);
                        binding.setName("hello world");
                        binding.executePendingBindings();
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw e;
                    }
                }
            });
            getInstrumentation().waitForIdleSync();
        } catch (Throwable t) {
            throw new Exception(t);
        }
    }

    public void testBindingLeak() throws Throwable {
        assertNotNull(mWeakReference.get());
        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                getActivity().setContentView(new FrameLayout(getActivity()));
            }
        });
        WeakReference<Object> canary = new WeakReference<Object>(new Object());
        while (canary.get() != null) {
            byte[] b = new byte[1024 * 1024];
            System.gc();
        }
        assertNull(mWeakReference.get());
    }

    // Test to ensure that when the View is detached that it doesn't rebind
    // the dirty Views. The rebind should happen only after the root view is
    // reattached.
    public void testNoChangeWhenDetached() throws Throwable {
        final LeakTestBinding binding = mWeakReference.get();
        final AnimationWatcher watcher = new AnimationWatcher();

        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                getActivity().setContentView(new FrameLayout(getActivity()));
                binding.setName("goodbye world");
                getActivity().getWindow().getDecorView().postOnAnimation(watcher);
            }
        });

        watcher.waitForAnimationThread();

        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                assertEquals("hello world", binding.textView.getText().toString());
                getActivity().setContentView(binding.getRoot());
                getActivity().getWindow().getDecorView().postOnAnimation(watcher);
            }
        });

        watcher.waitForAnimationThread();

        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                assertEquals("goodbye world", binding.textView.getText().toString());
            }
        });
    }

    private static class AnimationWatcher implements Runnable {
        private boolean mWaiting = true;

        public void waitForAnimationThread() throws InterruptedException {
            synchronized (this) {
                while (mWaiting) {
                    this.wait();
                }
                mWaiting = true;
            }
        }


        @Override
        public void run() {
            synchronized (this) {
                mWaiting = false;
                this.notifyAll();
            }
        }
    }
}
