/* * Copyright (C) 2017 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 com.android.bluetooth.mapclient; import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.SdpMasRecord; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.filters.Suppress; import android.support.test.runner.AndroidJUnit4; import android.util.Log; import com.android.bluetooth.R; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @Suppress // TODO: enable when b/74609188 is debugged @MediumTest @RunWith(AndroidJUnit4.class) public class MapClientStateMachineTest { private static final String TAG = "MapStateMachineTest"; private static final Integer TIMEOUT = 3000; private BluetoothAdapter mAdapter; private MceStateMachine mMceStateMachine = null; private BluetoothDevice mTestDevice; private Context mTargetContext; private FakeMapClientService mFakeMapClientService; private CountDownLatch mConnectedLatch = null; private CountDownLatch mDisconnectedLatch = null; private Handler mHandler; @Mock private MasClient mMockMasClient; @Before public void setUp() { MockitoAnnotations.initMocks(this); mTargetContext = InstrumentationRegistry.getTargetContext(); Assume.assumeTrue("Ignore test when MapClientService is not enabled", mTargetContext.getResources().getBoolean(R.bool.profile_supported_mapmce)); // This line must be called to make sure relevant objects are initialized properly mAdapter = BluetoothAdapter.getDefaultAdapter(); // Get a device for testing mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); mConnectedLatch = new CountDownLatch(1); mDisconnectedLatch = new CountDownLatch(1); mFakeMapClientService = new FakeMapClientService(); when(mMockMasClient.makeRequest(any(Request.class))).thenReturn(true); mMceStateMachine = new MceStateMachine(mFakeMapClientService, mTestDevice, mMockMasClient); Assert.assertNotNull(mMceStateMachine); if (Looper.myLooper() == null) { Looper.prepare(); } mHandler = new Handler(); } @After public void tearDown() { if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_mapmce)) { return; } if (mMceStateMachine != null) { mMceStateMachine.doQuit(); } } /** * Test that default state is STATE_CONNECTING */ @Test public void testDefaultConnectingState() { Log.i(TAG, "in testDefaultConnectingState"); Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mMceStateMachine.getState()); } /** * Test transition from * STATE_CONNECTING --> (receive MSG_MAS_DISCONNECTED) --> STATE_DISCONNECTED */ @Test public void testStateTransitionFromConnectingToDisconnected() { Log.i(TAG, "in testStateTransitionFromConnectingToDisconnected"); setupSdpRecordReceipt(); Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_DISCONNECTED); mMceStateMachine.getCurrentState().processMessage(msg); // Wait until the message is processed and a broadcast request is sent to // to MapClientService to change // state from STATE_CONNECTING to STATE_DISCONNECTED boolean result = false; try { result = mDisconnectedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } // Test that the latch reached zero; i.e., that a broadcast of state-change was received. Assert.assertTrue(result); // When the state reaches STATE_DISCONNECTED, MceStateMachine object is in the process of // being dismantled; i.e., can't rely on getting its current state. That means can't // test its current state = STATE_DISCONNECTED. } /** * Test transition from STATE_CONNECTING --> (receive MSG_MAS_CONNECTED) --> STATE_CONNECTED */ @Test public void testStateTransitionFromConnectingToConnected() { Log.i(TAG, "in testStateTransitionFromConnectingToConnected"); setupSdpRecordReceipt(); Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_CONNECTED); mMceStateMachine.getCurrentState().processMessage(msg); // Wait until the message is processed and a broadcast request is sent to // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED boolean result = false; try { result = mConnectedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } // Test that the latch reached zero; i.e., that a broadcast of state-change was received. Assert.assertTrue(result); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); } private void setupSdpRecordReceipt() { // Setup receipt of SDP record SdpMasRecord record = new SdpMasRecord(1, 1, 1, 1, 1, 1, "blah"); Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_SDP_DONE, record); mMceStateMachine.getCurrentState().processMessage(msg); } private class FakeMapClientService extends MapClientService { @Override void cleanupDevice(BluetoothDevice device) {} @Override public void sendBroadcast(Intent intent, String receiverPermission) { int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); Log.i(TAG, "received broadcast: prevState = " + prevState + ", state = " + state); if (prevState == BluetoothProfile.STATE_CONNECTING && state == BluetoothProfile.STATE_CONNECTED) { mConnectedLatch.countDown(); } else if (prevState == BluetoothProfile.STATE_CONNECTING && state == BluetoothProfile.STATE_DISCONNECTED) { mDisconnectedLatch.countDown(); } } } }