blob: 701666ca3f4d341a5f6d0b37e367463b822717e1 [file] [log] [blame]
Remi NGUYEN VANb9041132020-09-24 10:02:16 +09001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.testutils
18
19import android.Manifest.permission.MANAGE_TEST_NETWORKS
20import android.net.TestNetworkInterface
21import android.net.TestNetworkManager
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +090022import android.os.Handler
Remi NGUYEN VANb9041132020-09-24 10:02:16 +090023import android.os.HandlerThread
24import androidx.test.platform.app.InstrumentationRegistry
25import org.junit.rules.TestRule
26import org.junit.runner.Description
27import org.junit.runners.model.Statement
28import kotlin.test.assertFalse
29import kotlin.test.fail
30
31private const val HANDLER_TIMEOUT_MS = 10_000L
32
33/**
34 * A [TestRule] that sets up a [TapPacketReader] on a [TestNetworkInterface] for use in the test.
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +090035 *
36 * @param maxPacketSize Maximum size of packets read in the [TapPacketReader] buffer.
37 * @param autoStart Whether to initialize the interface and start the reader automatically for every
38 * test. If false, each test must either call start() and stop(), or be annotated
39 * with TapPacketReaderTest before using the reader or interface.
Remi NGUYEN VANb9041132020-09-24 10:02:16 +090040 */
41class TapPacketReaderRule @JvmOverloads constructor(
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +090042 private val maxPacketSize: Int = 1500,
43 private val autoStart: Boolean = true
Remi NGUYEN VANb9041132020-09-24 10:02:16 +090044) : TestRule {
45 // Use lateinit as the below members can't be initialized in the rule constructor (the
46 // InstrumentationRegistry may not be ready), but from the point of view of test cases using
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +090047 // this rule with autoStart = true, the members are always initialized (in setup/test/teardown):
48 // tests cases should be able use them directly.
49 // lateinit also allows getting good exceptions detailing what went wrong if the members are
50 // referenced before they could be initialized (typically if autoStart is false and the test
51 // does not call start or use @TapPacketReaderTest).
Remi NGUYEN VANb9041132020-09-24 10:02:16 +090052 lateinit var iface: TestNetworkInterface
53 lateinit var reader: TapPacketReader
54
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +090055 @Volatile
56 private var readerRunning = false
Remi NGUYEN VANb9041132020-09-24 10:02:16 +090057
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +090058 /**
59 * Indicates that the [TapPacketReaderRule] should initialize its [TestNetworkInterface] and
60 * start the [TapPacketReader] before the test, and tear them down afterwards.
61 *
62 * For use when [TapPacketReaderRule] is created with autoStart = false.
63 */
64 annotation class TapPacketReaderTest
65
66 /**
67 * Initialize the tap interface and start the [TapPacketReader].
68 *
69 * Tests using this method must also call [stop] before exiting.
70 * @param handler Handler to run the reader on. Callers are responsible for safely terminating
71 * the handler when the test ends. If null, a handler thread managed by the
72 * rule will be used.
73 */
74 @JvmOverloads
75 fun start(handler: Handler? = null) {
76 if (this::iface.isInitialized) {
77 fail("${TapPacketReaderRule::class.java.simpleName} was already started")
78 }
79
80 val ctx = InstrumentationRegistry.getInstrumentation().context
81 iface = runAsShell(MANAGE_TEST_NETWORKS) {
82 val tnm = ctx.getSystemService(TestNetworkManager::class.java)
83 ?: fail("Could not obtain the TestNetworkManager")
84 tnm.createTapInterface()
85 }
86 val usedHandler = handler ?: HandlerThread(
87 TapPacketReaderRule::class.java.simpleName).apply { start() }.threadHandler
88 reader = TapPacketReader(usedHandler, iface.fileDescriptor.fileDescriptor, maxPacketSize)
89 reader.startAsyncForTest()
90 readerRunning = true
Remi NGUYEN VANb9041132020-09-24 10:02:16 +090091 }
92
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +090093 /**
94 * Stop the [TapPacketReader].
95 *
96 * Tests calling [start] must call this method before exiting. If a handler was specified in
97 * [start], all messages on that handler must also be processed after calling this method and
98 * before exiting.
99 *
100 * If [start] was not called, calling this method is a no-op.
101 */
102 fun stop() {
103 // The reader may not be initialized if the test case did not use the rule, even though
104 // other test cases in the same class may be using it (so test classes may call stop in
105 // tearDown even if start is not called for all test cases).
106 if (!this::reader.isInitialized) return
107 reader.handler.post {
108 reader.stop()
109 readerRunning = false
110 }
111 }
Remi NGUYEN VANb9041132020-09-24 10:02:16 +0900112
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +0900113 override fun apply(base: Statement, description: Description): Statement {
114 return TapReaderStatement(base, description)
115 }
116
117 private inner class TapReaderStatement(
118 private val base: Statement,
119 private val description: Description
120 ) : Statement() {
121 override fun evaluate() {
122 val shouldStart = autoStart ||
123 description.getAnnotation(TapPacketReaderTest::class.java) != null
124 if (shouldStart) {
125 start()
126 }
Remi NGUYEN VANb9041132020-09-24 10:02:16 +0900127
128 try {
129 base.evaluate()
130 } finally {
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +0900131 if (shouldStart) {
132 stop()
133 reader.handler.looper.apply {
134 quitSafely()
135 thread.join(HANDLER_TIMEOUT_MS)
136 assertFalse(thread.isAlive,
137 "HandlerThread did not exit within $HANDLER_TIMEOUT_MS ms")
138 }
139 }
140
141 if (this@TapPacketReaderRule::iface.isInitialized) {
142 iface.fileDescriptor.close()
143 }
Remi NGUYEN VANb9041132020-09-24 10:02:16 +0900144 }
Remi NGUYEN VAN539aece2020-09-25 13:03:42 +0900145
146 assertFalse(readerRunning,
147 "stop() was not called, or the provided handler did not process the stop " +
148 "message before the test ended. If not using autostart, make sure to call " +
149 "stop() after the test. If a handler is specified in start(), make sure all " +
150 "messages are processed after calling stop(), before quitting (for example " +
151 "by using HandlerThread#quitSafely and HandlerThread#join).")
Remi NGUYEN VANb9041132020-09-24 10:02:16 +0900152 }
153 }
154}