Add module tests

This commit is contained in:
topjohnwu 2025-02-13 19:10:42 +08:00 committed by John Wu
parent 6c05f2ae85
commit f5f9b285c0
5 changed files with 151 additions and 31 deletions

View File

@ -14,7 +14,7 @@ object Const {
else Build.SUPPORTED_32_BIT_ABIS.firstOrNull()
// Paths
const val MAGISK_PATH = "/data/adb/modules"
const val MODULE_PATH = "/data/adb/modules"
const val TMPDIR = "/dev/tmp"
const val MAGISK_LOG = "/cache/magisk.log"

View File

@ -5,6 +5,7 @@ import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.nio.ExtendedFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -12,7 +13,7 @@ import java.io.IOException
import java.util.Locale
data class LocalModule(
private val path: String,
private val base: ExtendedFile,
) : Module() {
private val svc get() = ServiceLocator.networkService
@ -24,20 +25,18 @@ data class LocalModule(
var description: String = ""
var updateInfo: OnlineModule? = null
var outdated = false
private var updateUrl: String = ""
private val removeFile = RootUtils.fs.getFile(path, "remove")
private val disableFile = RootUtils.fs.getFile(path, "disable")
private val updateFile = RootUtils.fs.getFile(path, "update")
private val riruFolder = RootUtils.fs.getFile(path, "riru")
private val zygiskFolder = RootUtils.fs.getFile(path, "zygisk")
private val unloaded = RootUtils.fs.getFile(zygiskFolder, "unloaded")
val updated: Boolean get() = updateFile.exists()
val isRiru: Boolean get() = (id == "riru-core") || riruFolder.exists()
val isZygisk: Boolean get() = zygiskFolder.exists()
val zygiskUnloaded: Boolean get() = unloaded.exists()
val hasAction: Boolean;
private val removeFile = base.getChildFile("remove")
private val disableFile = base.getChildFile("disable")
private val updateFile = base.getChildFile("update")
val zygiskFolder = base.getChildFile("zygisk")
val updated get() = updateFile.exists()
val isRiru = (id == "riru-core") || base.getChildFile("riru").exists()
val isZygisk = zygiskFolder.exists()
val zygiskUnloaded = zygiskFolder.getChildFile("unloaded").exists()
val hasAction = base.getChildFile("action.sh").exists()
var enable: Boolean
get() = !disableFile.exists()
@ -90,19 +89,16 @@ data class LocalModule(
init {
runCatching {
parseProps(Shell.cmd("dos2unix < $path/module.prop").exec().out)
parseProps(Shell.cmd("dos2unix < $base/module.prop").exec().out)
}
if (id.isEmpty()) {
val sep = path.lastIndexOf('/')
id = path.substring(sep + 1)
id = base.name
}
if (name.isEmpty()) {
name = id
}
hasAction = RootUtils.fs.getFile(path, "action.sh").exists()
}
suspend fun fetch(): Boolean {
@ -125,14 +121,14 @@ data class LocalModule(
companion object {
fun loaded() = RootUtils.fs.getFile(Const.MAGISK_PATH).exists()
fun loaded() = RootUtils.fs.getFile(Const.MODULE_PATH).exists()
suspend fun installed() = withContext(Dispatchers.IO) {
RootUtils.fs.getFile(Const.MAGISK_PATH)
RootUtils.fs.getFile(Const.MODULE_PATH)
.listFiles()
.orEmpty()
.filter { !it.isFile && !it.isHidden }
.map { LocalModule("${Const.MAGISK_PATH}/${it.name}") }
.map { LocalModule(it) }
.sortedBy { it.name.lowercase(Locale.ROOT) }
}
}

View File

@ -5,9 +5,17 @@ import androidx.annotation.Keep
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.utils.RootUtils
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assume.assumeTrue
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.TimeUnit
@ -21,6 +29,17 @@ class AdditionalTest : BaseTest {
private const val SHELL_PKG = "com.android.shell"
private const val LSPOSED_CATEGORY = "org.lsposed.manager.LAUNCH_MANAGER"
private const val LSPOSED_PKG = "org.lsposed.manager"
private lateinit var modules: List<LocalModule>
@BeforeClass
@JvmStatic
fun before() {
BaseTest.prerequisite()
runBlocking {
modules = LocalModule.installed()
}
}
}
@After
@ -29,9 +48,23 @@ class AdditionalTest : BaseTest {
}
@Test
fun testLaunchLsposedManager() {
fun testModuleCount() {
var expected = 2
if (Environment.lsposed()) expected++
if (Environment.shamiko()) expected++
assertEquals("Module count incorrect", expected, modules.size)
}
@Test
fun testLsposed() {
assumeTrue(Environment.lsposed())
val module = modules.find { it.id == "zygisk_lsposed" }
assertNotNull("zygisk_lsposed is not installed", module)
module!!
assertFalse("zygisk_lsposed is not enabled", module.zygiskUnloaded)
// Launch lsposed manager to ensure the module is active
uiAutomation.executeShellCommand(
"am start -c $LSPOSED_CATEGORY $SHELL_PKG/.BugreportWarningActivity"
).let { pfd -> AutoCloseInputStream(pfd).use { it.readBytes() } }
@ -42,4 +75,33 @@ class AdditionalTest : BaseTest {
device.wait(Until.hasObject(By.res(pattern)), TimeUnit.SECONDS.toMillis(10))
)
}
@Test
fun testModule01() {
val module = modules.find { it.id == "test_01" }
assertNotNull("test_01 is not installed", module)
assertTrue(
"/system/etc/newfile should exist",
RootUtils.fs.getFile("/system/etc/newfile").exists()
)
assertFalse(
"/system/bin/screenrecord should not exist",
RootUtils.fs.getFile("/system/bin/screenrecord").exists()
)
module!!
assertTrue("test_01 should be zygisk unloaded", module.zygiskUnloaded)
}
@Test
fun testModule02() {
val module = modules.find { it.id == "test_02" }
assertNotNull("test_02 is not installed", module)
module!!
assertTrue("test_02 should be zygisk unloaded", module.zygiskUnloaded)
}
@Test
fun testModule03() {
assertNull("test_03 should be removed", modules.find { it.id == "test_03" })
}
}

View File

@ -6,13 +6,18 @@ import androidx.annotation.Keep
import androidx.core.net.toUri
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.topjohnwu.magisk.core.BuildConfig.APP_PACKAGE_NAME
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.download.DownloadNotifier
import com.topjohnwu.magisk.core.download.DownloadProcessor
import com.topjohnwu.magisk.core.ktx.cachedFile
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.tasks.AppMigration
import com.topjohnwu.magisk.core.tasks.FlashZip
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.nio.ExtendedFile
import kotlinx.coroutines.runBlocking
import org.apache.commons.compress.archivers.zip.ZipFile
import org.junit.Assert.assertArrayEquals
@ -37,7 +42,7 @@ class Environment : BaseTest {
return Build.VERSION.SDK_INT >= 27 && Build.VERSION.SDK_INT <= 34
}
private fun shamiko(): Boolean {
fun shamiko(): Boolean {
return Build.VERSION.SDK_INT >= 27
}
@ -72,6 +77,55 @@ class Environment : BaseTest {
}
}
private fun setupModule01(root: ExtendedFile) {
val error = "test_01 setup failed"
val path = root.getChildFile("test_01")
// Create /system/etc/newfile
val etc = path.getChildFile("system").getChildFile("etc")
assertTrue(error, etc.mkdirs())
assertTrue(error, etc.getChildFile("newfile").createNewFile())
// Delete /system/bin/screenrecord
val bin = path.getChildFile("system").getChildFile("bin")
assertTrue(error, bin.mkdirs())
assertTrue(error, Shell.cmd("mknod $bin/screenrecord c 0 0").exec().isSuccess)
// Create an empty zygisk folder
val module = LocalModule(path)
assertTrue(error, module.zygiskFolder.mkdir())
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
private fun setupModule02(root: ExtendedFile) {
val error = "test_02 setup failed"
val path = root.getChildFile("test_02")
// Create invalid zygisk libraries
val module = LocalModule(path)
assertTrue(error, module.zygiskFolder.mkdirs())
assertTrue(error, module.zygiskFolder.getChildFile("armeabi-v7a.so").createNewFile())
assertTrue(error, module.zygiskFolder.getChildFile("arm64-v8a.so").createNewFile())
assertTrue(error, module.zygiskFolder.getChildFile("x86.so").createNewFile())
assertTrue(error, module.zygiskFolder.getChildFile("x86_64.so").createNewFile())
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
private fun setupModule03(root: ExtendedFile) {
val error = "test_03 setup failed"
val path = root.getChildFile("test_03")
// Create a new module but mark is as "remove"
val module = LocalModule(path)
assertTrue(error, path.mkdirs())
assertTrue(error, path.getChildFile("service.sh").createNewFile())
module.remove = true
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
@Test
fun setupEnvironment() {
runBlocking {
@ -114,6 +168,11 @@ class Environment : BaseTest {
)
}
}
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
setupModule01(root)
setupModule02(root)
setupModule03(root)
}
@Test

View File

@ -621,6 +621,15 @@ is_legacy_script() {
return $?
}
# $1 = MODPATH
set_default_perm() {
set_perm_recursive $1 0 0 0755 0644
set_perm_recursive $1/system/bin 0 2000 0755 0755
set_perm_recursive $1/system/xbin 0 2000 0755 0755
set_perm_recursive $1/system/system_ext/bin 0 2000 0755 0755
set_perm_recursive $1/system/vendor/bin 0 2000 0755 0755 u:object_r:vendor_file:s0
}
# Require OUTFD, ZIPFILE to be set
install_module() {
rm -rf $TMPDIR
@ -683,13 +692,7 @@ install_module() {
if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null; then
ui_print "- Extracting module files"
unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2
# Default permissions
set_perm_recursive $MODPATH 0 0 0755 0644
set_perm_recursive $MODPATH/system/bin 0 2000 0755 0755
set_perm_recursive $MODPATH/system/xbin 0 2000 0755 0755
set_perm_recursive $MODPATH/system/system_ext/bin 0 2000 0755 0755
set_perm_recursive $MODPATH/system/vendor/bin 0 2000 0755 0755 u:object_r:vendor_file:s0
set_default_perm $MODPATH
fi
# Load customization script