blob: 3319fbe1975fd94b4f9c978aba7ce42e44ef8de5 [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
22import android.os.HandlerThread
23import androidx.test.platform.app.InstrumentationRegistry
24import org.junit.rules.TestRule
25import org.junit.runner.Description
26import org.junit.runners.model.Statement
27import kotlin.test.assertFalse
28import kotlin.test.fail
29
30private const val HANDLER_TIMEOUT_MS = 10_000L
31
32/**
33 * A [TestRule] that sets up a [TapPacketReader] on a [TestNetworkInterface] for use in the test.
34 */
35class TapPacketReaderRule @JvmOverloads constructor(
36 private val maxPacketSize: Int = 1500
37) : TestRule {
38 // Use lateinit as the below members can't be initialized in the rule constructor (the
39 // InstrumentationRegistry may not be ready), but from the point of view of test cases using
40 // this rule, the members are always initialized (in setup/test/teardown): tests cases should be
41 // able use them directly.
42 // lateinit also allows getting good exceptions detailing what went wrong in the unlikely event
43 // that the members are referenced before they could be initialized.
44 lateinit var iface: TestNetworkInterface
45 lateinit var reader: TapPacketReader
46
47 // The reader runs on its own handlerThread created locally, but this is not an actual
48 // requirement: any handler could be used for this rule. If using a specific handler is needed,
49 // a method could be added to start the TapPacketReader manually on a given handler.
50 private val handlerThread = HandlerThread(TapPacketReaderRule::class.java.simpleName)
51
52 override fun apply(base: Statement, description: Description): Statement {
53 return TapReaderStatement(base)
54 }
55
56 private inner class TapReaderStatement(private val base: Statement) : Statement() {
57 override fun evaluate() {
58 val ctx: android.content.Context = InstrumentationRegistry.getInstrumentation().context
59 iface = runAsShell(MANAGE_TEST_NETWORKS) {
60 val tnm = ctx.getSystemService(TestNetworkManager::class.java)
61 ?: fail("Could not obtain the TestNetworkManager")
62 tnm.createTapInterface()
63 }
64
65 handlerThread.start()
66 reader = TapPacketReader(handlerThread.threadHandler,
67 iface.fileDescriptor.fileDescriptor, maxPacketSize)
68 reader.startAsyncForTest()
69
70 try {
71 base.evaluate()
72 } finally {
73 handlerThread.threadHandler.post(reader::stop)
74 handlerThread.quitSafely()
75 handlerThread.join(HANDLER_TIMEOUT_MS)
76 assertFalse(handlerThread.isAlive,
77 "HandlerThread did not exit within $HANDLER_TIMEOUT_MS ms")
78 iface.fileDescriptor.close()
79 }
80 }
81 }
82}