Merge changes I427360f6,Ie6dbbe22 into main
* changes: SyncSM05.1: add testMultiDepthTransition SyncSM05: add SyncStateMachineTest
This commit is contained in:
@@ -0,0 +1,294 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2023 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.networkstack.tethering.util
|
||||||
|
|
||||||
|
import android.os.Message
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.internal.util.State
|
||||||
|
import com.android.networkstack.tethering.util.SyncStateMachine.StateInfo
|
||||||
|
import java.util.ArrayDeque
|
||||||
|
import java.util.ArrayList
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers.any
|
||||||
|
import org.mockito.Mockito.inOrder
|
||||||
|
import org.mockito.Mockito.spy
|
||||||
|
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||||
|
|
||||||
|
private const val MSG_INVALID = -1
|
||||||
|
private const val MSG_1 = 1
|
||||||
|
private const val MSG_2 = 2
|
||||||
|
private const val MSG_3 = 3
|
||||||
|
private const val MSG_4 = 4
|
||||||
|
private const val MSG_5 = 5
|
||||||
|
private const val MSG_6 = 6
|
||||||
|
private const val MSG_7 = 7
|
||||||
|
private const val ARG_1 = 100
|
||||||
|
private const val ARG_2 = 200
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@SmallTest
|
||||||
|
class SynStateMachineTest {
|
||||||
|
private val mState1 = spy(object : TestState(MSG_1) {})
|
||||||
|
private val mState2 = spy(object : TestState(MSG_2) {})
|
||||||
|
private val mState3 = spy(object : TestState(MSG_3) {})
|
||||||
|
private val mState4 = spy(object : TestState(MSG_4) {})
|
||||||
|
private val mState5 = spy(object : TestState(MSG_5) {})
|
||||||
|
private val mState6 = spy(object : TestState(MSG_6) {})
|
||||||
|
private val mState7 = spy(object : TestState(MSG_7) {})
|
||||||
|
private val mInOrder = inOrder(mState1, mState2, mState3, mState4, mState5, mState6, mState7)
|
||||||
|
// Lazy initialize to make sure running in test thread.
|
||||||
|
private val mSM by lazy {
|
||||||
|
SyncStateMachine("TestSyncStateMachine", Thread.currentThread(), true /* debug */)
|
||||||
|
}
|
||||||
|
private val mAllStates = ArrayList<StateInfo>()
|
||||||
|
|
||||||
|
private val mMsgProcessedResults = ArrayDeque<Pair<State, Int>>()
|
||||||
|
|
||||||
|
open inner class TestState(val expected: Int) : State() {
|
||||||
|
// Control destination state in obj field for testing.
|
||||||
|
override fun processMessage(msg: Message): Boolean {
|
||||||
|
mMsgProcessedResults.add(this to msg.what)
|
||||||
|
assertEquals(ARG_1, msg.arg1)
|
||||||
|
assertEquals(ARG_2, msg.arg2)
|
||||||
|
|
||||||
|
if (msg.what == expected) {
|
||||||
|
msg.obj?.let { mSM.transitionTo(it as State) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyNoMoreInteractions() {
|
||||||
|
verifyNoMoreInteractions(mState1, mState2, mState3, mState4, mState5, mState6)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processMessage(what: Int, toState: State?) {
|
||||||
|
mSM.processMessage(what, ARG_1, ARG_2, toState)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyMessageProcessedBy(what: Int, vararg processedStates: State) {
|
||||||
|
for (state in processedStates) {
|
||||||
|
// InOrder.verify can't check the Message content here because SyncSM will recycle the
|
||||||
|
// message after it's been processed. SyncSM reuses the same Message instance for all
|
||||||
|
// messages it processes. So, if using InOrder.verify to verify the content of a message
|
||||||
|
// after SyncSM has processed it, the content would be wrong.
|
||||||
|
mInOrder.verify(state).processMessage(any())
|
||||||
|
val (processedState, msgWhat) = mMsgProcessedResults.remove()
|
||||||
|
assertEquals(state, processedState)
|
||||||
|
assertEquals(what, msgWhat)
|
||||||
|
}
|
||||||
|
assertTrue(mMsgProcessedResults.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInitialState() {
|
||||||
|
// mState1 -> initial
|
||||||
|
// |
|
||||||
|
// mState2
|
||||||
|
mAllStates.add(StateInfo(mState1, null))
|
||||||
|
mAllStates.add(StateInfo(mState2, mState1))
|
||||||
|
mSM.addAllStates(mAllStates)
|
||||||
|
|
||||||
|
mSM.start(mState1)
|
||||||
|
mInOrder.verify(mState1).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testStartFromLeafState() {
|
||||||
|
// mState1 -> initial
|
||||||
|
// |
|
||||||
|
// mState2
|
||||||
|
// |
|
||||||
|
// mState3
|
||||||
|
mAllStates.add(StateInfo(mState1, null))
|
||||||
|
mAllStates.add(StateInfo(mState2, mState1))
|
||||||
|
mAllStates.add(StateInfo(mState3, mState2))
|
||||||
|
mSM.addAllStates(mAllStates)
|
||||||
|
|
||||||
|
mSM.start(mState3)
|
||||||
|
mInOrder.verify(mState1).enter()
|
||||||
|
mInOrder.verify(mState2).enter()
|
||||||
|
mInOrder.verify(mState3).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyStart() {
|
||||||
|
mSM.addAllStates(mAllStates)
|
||||||
|
mSM.start(mState1)
|
||||||
|
mInOrder.verify(mState1).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addState(state: State, parent: State? = null) {
|
||||||
|
mAllStates.add(StateInfo(state, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddState() {
|
||||||
|
// Add duplicated states.
|
||||||
|
mAllStates.add(StateInfo(mState1, null))
|
||||||
|
mAllStates.add(StateInfo(mState1, null))
|
||||||
|
assertFailsWith(IllegalStateException::class) {
|
||||||
|
mSM.addAllStates(mAllStates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testProcessMessage() {
|
||||||
|
// mState1
|
||||||
|
// |
|
||||||
|
// mState2
|
||||||
|
addState(mState1)
|
||||||
|
addState(mState2, mState1)
|
||||||
|
verifyStart()
|
||||||
|
|
||||||
|
processMessage(MSG_1, null)
|
||||||
|
verifyMessageProcessedBy(MSG_1, mState1)
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTwoStates() {
|
||||||
|
// mState1 <-initial, mState2
|
||||||
|
addState(mState1)
|
||||||
|
addState(mState2)
|
||||||
|
verifyStart()
|
||||||
|
|
||||||
|
// Test transition to mState2
|
||||||
|
processMessage(MSG_1, mState2)
|
||||||
|
verifyMessageProcessedBy(MSG_1, mState1)
|
||||||
|
mInOrder.verify(mState1).exit()
|
||||||
|
mInOrder.verify(mState2).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
|
||||||
|
// If set destState to mState2 (current state), no state transition.
|
||||||
|
processMessage(MSG_2, mState2)
|
||||||
|
verifyMessageProcessedBy(MSG_2, mState2)
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTwoStateTrees() {
|
||||||
|
// mState1 -> initial mState4
|
||||||
|
// / \ / \
|
||||||
|
// mState2 mState3 mState5 mState6
|
||||||
|
addState(mState1)
|
||||||
|
addState(mState2, mState1)
|
||||||
|
addState(mState3, mState1)
|
||||||
|
addState(mState4)
|
||||||
|
addState(mState5, mState4)
|
||||||
|
addState(mState6, mState4)
|
||||||
|
verifyStart()
|
||||||
|
|
||||||
|
// mState1 -> current mState4
|
||||||
|
// / \ / \
|
||||||
|
// mState2 mState3 -> dest mState5 mState6
|
||||||
|
processMessage(MSG_1, mState3)
|
||||||
|
verifyMessageProcessedBy(MSG_1, mState1)
|
||||||
|
mInOrder.verify(mState3).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
|
||||||
|
// mState1 mState4
|
||||||
|
// / \ / \
|
||||||
|
// dest <- mState2 mState3 -> current mState5 mState6
|
||||||
|
processMessage(MSG_1, mState2)
|
||||||
|
verifyMessageProcessedBy(MSG_1, mState3, mState1)
|
||||||
|
mInOrder.verify(mState3).exit()
|
||||||
|
mInOrder.verify(mState2).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
|
||||||
|
// mState1 mState4
|
||||||
|
// / \ / \
|
||||||
|
// current <- mState2 mState3 mState5 mState6 -> dest
|
||||||
|
processMessage(MSG_2, mState6)
|
||||||
|
verifyMessageProcessedBy(MSG_2, mState2)
|
||||||
|
mInOrder.verify(mState2).exit()
|
||||||
|
mInOrder.verify(mState1).exit()
|
||||||
|
mInOrder.verify(mState4).enter()
|
||||||
|
mInOrder.verify(mState6).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMultiDepthTransition() {
|
||||||
|
// mState1 -> current
|
||||||
|
// | \
|
||||||
|
// mState2 mState6
|
||||||
|
// | \ |
|
||||||
|
// mState3 mState5 mState7
|
||||||
|
// |
|
||||||
|
// mState4
|
||||||
|
addState(mState1)
|
||||||
|
addState(mState2, mState1)
|
||||||
|
addState(mState6, mState1)
|
||||||
|
addState(mState3, mState2)
|
||||||
|
addState(mState5, mState2)
|
||||||
|
addState(mState7, mState6)
|
||||||
|
addState(mState4, mState3)
|
||||||
|
verifyStart()
|
||||||
|
|
||||||
|
// mState1 -> current
|
||||||
|
// | \
|
||||||
|
// mState2 mState6
|
||||||
|
// | \ |
|
||||||
|
// mState3 mState5 mState7
|
||||||
|
// |
|
||||||
|
// mState4 -> dest
|
||||||
|
processMessage(MSG_1, mState4)
|
||||||
|
verifyMessageProcessedBy(MSG_1, mState1)
|
||||||
|
mInOrder.verify(mState2).enter()
|
||||||
|
mInOrder.verify(mState3).enter()
|
||||||
|
mInOrder.verify(mState4).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
|
||||||
|
// mState1
|
||||||
|
// / \
|
||||||
|
// mState2 mState6
|
||||||
|
// | \ \
|
||||||
|
// mState3 mState5 -> dest mState7
|
||||||
|
// |
|
||||||
|
// mState4 -> current
|
||||||
|
processMessage(MSG_1, mState5)
|
||||||
|
verifyMessageProcessedBy(MSG_1, mState4, mState3, mState2, mState1)
|
||||||
|
mInOrder.verify(mState4).exit()
|
||||||
|
mInOrder.verify(mState3).exit()
|
||||||
|
mInOrder.verify(mState5).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
|
||||||
|
// mState1
|
||||||
|
// / \
|
||||||
|
// mState2 mState6
|
||||||
|
// | \ \
|
||||||
|
// mState3 mState5 -> current mState7 -> dest
|
||||||
|
// |
|
||||||
|
// mState4
|
||||||
|
processMessage(MSG_2, mState7)
|
||||||
|
verifyMessageProcessedBy(MSG_2, mState5, mState2)
|
||||||
|
mInOrder.verify(mState5).exit()
|
||||||
|
mInOrder.verify(mState2).exit()
|
||||||
|
mInOrder.verify(mState6).enter()
|
||||||
|
mInOrder.verify(mState7).enter()
|
||||||
|
verifyNoMoreInteractions()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user