// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.mojo.bindings;

import androidx.test.filters.SmallTest;

import org.junit.After;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.mojo.MojoTestRule;
import org.chromium.mojo.bindings.Callbacks.Callback1;
import org.chromium.mojo.bindings.test.mojom.sample.IntegerAccessor;
import org.chromium.mojo.system.MojoException;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Tests for interface control messages.
 */
@RunWith(BaseJUnit4ClassRunner.class)
public class InterfaceControlMessageTest {
    @Rule
    public MojoTestRule mTestRule = new MojoTestRule();

    private final List<Closeable> mCloseablesToClose = new ArrayList<Closeable>();

    /**
     * See mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.
     */
    class IntegerAccessorImpl extends SideEffectFreeCloseable implements IntegerAccessor {
        private long mValue;
        private int mEnum;
        private boolean mEncounteredError;

        /**
         * @see ConnectionErrorHandler#onConnectionError(MojoException)
         */
        @Override
        public void onConnectionError(MojoException e) {
            mEncounteredError = true;
        }

        /**
         * @see IntegerAccessor#getInteger(IntegerAccessor.GetIntegerResponse)
         */
        @Override
        public void getInteger(GetIntegerResponse response) {
            response.call(mValue, mEnum);
        }

        /**
         * @see IntegerAccessor#setInteger(long, int)
         */
        @Override
        public void setInteger(long value, int enumValue) {
            mValue = value;
            mEnum = enumValue;
        }

        public long getValue() {
            return mValue;
        }

        public boolean encounteredError() {
            return mEncounteredError;
        }
    }

    /**
     * @see MojoTestCase#tearDown()
     */
    @After
    public void tearDown() throws Exception {
        // Close the elements in the reverse order they were added. This is needed because it is an
        // error to close the handle of a proxy without closing the proxy first.
        Collections.reverse(mCloseablesToClose);
        for (Closeable c : mCloseablesToClose) {
            c.close();
        }
    }

    @Test
    @SmallTest
    public void testQueryVersion() {
        IntegerAccessor.Proxy p = BindingsTestUtils.newProxyOverPipe(
                IntegerAccessor.MANAGER, new IntegerAccessorImpl(), mCloseablesToClose);
        Assert.assertEquals(0, p.getProxyHandler().getVersion());
        p.getProxyHandler().queryVersion(new Callback1<Integer>() {
            @Override
            public void call(Integer version) {
                Assert.assertEquals(3, version.intValue());
            }
        });
        mTestRule.runLoopUntilIdle();
        Assert.assertEquals(3, p.getProxyHandler().getVersion());
    }

    @Test
    @SmallTest
    public void testRequireVersion() {
        IntegerAccessorImpl impl = new IntegerAccessorImpl();
        IntegerAccessor.Proxy p = BindingsTestUtils.newProxyOverPipe(
                IntegerAccessor.MANAGER, impl, mCloseablesToClose);

        Assert.assertEquals(0, p.getProxyHandler().getVersion());

        p.getProxyHandler().requireVersion(1);
        Assert.assertEquals(1, p.getProxyHandler().getVersion());
        p.setInteger(123, Enum.VALUE);
        mTestRule.runLoopUntilIdle();
        Assert.assertFalse(impl.encounteredError());
        Assert.assertEquals(123, impl.getValue());

        p.getProxyHandler().requireVersion(3);
        Assert.assertEquals(3, p.getProxyHandler().getVersion());
        p.setInteger(456, Enum.VALUE);
        mTestRule.runLoopUntilIdle();
        Assert.assertFalse(impl.encounteredError());
        Assert.assertEquals(456, impl.getValue());

        // Require a version that is not supported by the implementation side.
        p.getProxyHandler().requireVersion(4);
        // getVersion() is updated synchronously.
        Assert.assertEquals(4, p.getProxyHandler().getVersion());
        p.setInteger(789, Enum.VALUE);
        mTestRule.runLoopUntilIdle();
        Assert.assertTrue(impl.encounteredError());
        // The call to setInteger() after requireVersion() is ignored.
        Assert.assertEquals(456, impl.getValue());
    }
}
