1
mirror of https://github.com/revanced/revanced-patcher synced 2025-09-13 18:30:49 +02:00

Compare commits

..

31 Commits

Author SHA1 Message Date
semantic-release-bot
1bafb77355 chore(release): 4.2.0 [skip ci]
# [4.2.0](https://github.com/revanced/revanced-patcher/compare/v4.1.5...v4.2.0) (2022-09-08)

### Bug Fixes

* remove repeatable from PatchDeprecated ([6e73631](6e73631d4d))

### Features

* SincePatcher annotation ([25f74dc](25f74dc5e9))
2022-09-08 12:43:11 +00:00
Sculas
25f74dc5e9 feat: SincePatcher annotation 2022-09-08 14:41:42 +02:00
Sculas
6e73631d4d fix: remove repeatable from PatchDeprecated 2022-09-08 14:00:15 +02:00
semantic-release-bot
7761d5b85e chore(release): 4.1.5 [skip ci]
## [4.1.5](https://github.com/revanced/revanced-patcher/compare/v4.1.4...v4.1.5) (2022-09-08)

### Bug Fixes

* broken deprecation message ([62aa295](62aa295e73))
2022-09-08 11:43:39 +00:00
Sculas
62aa295e73 fix: broken deprecation message 2022-09-08 13:42:26 +02:00
Sculas
596ede1b12 refactor: make patchName work on any class 2022-09-08 13:42:15 +02:00
semantic-release-bot
7debe62738 chore(release): 4.1.4 [skip ci]
## [4.1.4](https://github.com/revanced/revanced-patcher/compare/v4.1.3...v4.1.4) (2022-09-08)

### Bug Fixes

* handle option types and nulls properly ([aff4968](aff4968e6f))
2022-09-08 09:30:49 +00:00
Sculas
002f84da1a Merge remote-tracking branch 'origin/main' into main 2022-09-08 11:29:14 +02:00
Sculas
aff4968e6f fix: handle option types and nulls properly 2022-09-08 11:29:06 +02:00
Sculas
1d989abd55 chore: ignore kotlinc 2022-09-08 11:07:34 +02:00
semantic-release-bot
f1775f83d0 chore(release): 4.1.3 [skip ci]
## [4.1.3](https://github.com/revanced/revanced-patcher/compare/v4.1.2...v4.1.3) (2022-09-07)

### Bug Fixes

* only run list option check if not null ([4055939](4055939c08))
2022-09-07 21:48:24 +00:00
Sculas
4055939c08 fix: only run list option check if not null 2022-09-07 23:46:46 +02:00
semantic-release-bot
85120374d6 chore(release): 4.1.2 [skip ci]
## [4.1.2](https://github.com/revanced/revanced-patcher/compare/v4.1.1...v4.1.2) (2022-09-07)

### Bug Fixes

* invalid types for example options ([79f91e0](79f91e0e5a))
2022-09-07 21:24:50 +00:00
Sculas
8b4819faa1 Merge remote-tracking branch 'origin/main' into main 2022-09-07 23:23:29 +02:00
semantic-release-bot
d219276298 chore(release): 4.1.1 [skip ci]
## [4.1.1](https://github.com/revanced/revanced-patcher/compare/v4.1.0...v4.1.1) (2022-09-07)

### Bug Fixes

* handle private companion objects ([ad3d332](ad3d332e27))
2022-09-07 21:23:12 +00:00
Sculas
79f91e0e5a fix: invalid types for example options 2022-09-07 23:22:34 +02:00
Sculas
fadf62f594 Merge remote-tracking branch 'origin/main' into main 2022-09-07 23:21:41 +02:00
Sculas
ad3d332e27 fix: handle private companion objects 2022-09-07 23:21:32 +02:00
semantic-release-bot
8f66df7666 chore(release): 4.1.0 [skip ci]
# [4.1.0](https://github.com/revanced/revanced-patcher/compare/v4.0.0...v4.1.0) (2022-09-07)

### Features

* deprecation for patches ([80c2e80](80c2e80925))
2022-09-07 20:32:51 +00:00
Sculas
80c2e80925 feat: deprecation for patches 2022-09-07 22:31:15 +02:00
semantic-release-bot
c3db23d3c7 chore(release): 4.0.0 [skip ci]
# [4.0.0](https://github.com/revanced/revanced-patcher/compare/v3.5.1...v4.0.0) (2022-09-07)

### Code Refactoring

* Improve Patch Options ([6b909c1](6b909c1ee6))

### BREAKING CHANGES

* Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
2022-09-07 18:57:04 +00:00
Sculas
c28584736e Merge remote-tracking branch 'origin/main' into main 2022-09-07 20:55:45 +02:00
Sculas
6b909c1ee6 refactor: Improve Patch Options
It's so much better now. Really happy with the current system.

BREAKING CHANGE: Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
2022-09-07 20:55:35 +02:00
Sculas
0e8446516e build: add Kotlin Reflect 2022-09-07 20:52:05 +02:00
semantic-release-bot
aa46b953db chore(release): 3.5.1 [skip ci]
## [3.5.1](https://github.com/revanced/revanced-patcher/compare/v3.5.0...v3.5.1) (2022-09-06)

### Bug Fixes

* add tests for PathOption ([d6308e1](d6308e126c))
* PathOption should be open, not sealed ([a562e47](a562e476c0))
* typo in ListOption ([3921648](392164862c))

### Performance Improvements

* make exception an object ([75d2be8](75d2be8803))
2022-09-06 20:38:24 +00:00
Sculas
a562e476c0 fix: PathOption should be open, not sealed 2022-09-06 22:36:20 +02:00
Sculas
75d2be8803 perf: make exception an object 2022-09-06 22:35:33 +02:00
Sculas
d6308e126c fix: add tests for PathOption 2022-09-06 22:35:00 +02:00
Sculas
bb97af4d86 refactor: add FileOption alias for PathOption 2022-09-06 22:34:46 +02:00
Sculas
392164862c fix: typo in ListOption 2022-09-06 21:44:05 +02:00
Sculas
53e807dec1 refactor: add PatchOption.PathOption 2022-09-06 21:43:45 +02:00
15 changed files with 323 additions and 37 deletions

1
.idea/.gitignore generated vendored
View File

@@ -6,3 +6,4 @@
# Datasource local storage ignored files # Datasource local storage ignored files
/dataSources/ /dataSources/
/dataSources.local.xml /dataSources.local.xml
/kotlinc.xml

View File

@@ -1,3 +1,83 @@
# [4.2.0](https://github.com/revanced/revanced-patcher/compare/v4.1.5...v4.2.0) (2022-09-08)
### Bug Fixes
* remove repeatable from PatchDeprecated ([6e73631](https://github.com/revanced/revanced-patcher/commit/6e73631d4d21e5e862f07ed7517244f36394e5ca))
### Features
* SincePatcher annotation ([25f74dc](https://github.com/revanced/revanced-patcher/commit/25f74dc5e9ed1a09258345b920d4f5a0dd7da527))
## [4.1.5](https://github.com/revanced/revanced-patcher/compare/v4.1.4...v4.1.5) (2022-09-08)
### Bug Fixes
* broken deprecation message ([62aa295](https://github.com/revanced/revanced-patcher/commit/62aa295e7372014238415af36d902a4e88e2acbc))
## [4.1.4](https://github.com/revanced/revanced-patcher/compare/v4.1.3...v4.1.4) (2022-09-08)
### Bug Fixes
* handle option types and nulls properly ([aff4968](https://github.com/revanced/revanced-patcher/commit/aff4968e6f67239afa3b5c02cc133a17d9c3cbeb))
## [4.1.3](https://github.com/revanced/revanced-patcher/compare/v4.1.2...v4.1.3) (2022-09-07)
### Bug Fixes
* only run list option check if not null ([4055939](https://github.com/revanced/revanced-patcher/commit/4055939c089e3c396c308c980215d93a1dea5954))
## [4.1.2](https://github.com/revanced/revanced-patcher/compare/v4.1.1...v4.1.2) (2022-09-07)
### Bug Fixes
* invalid types for example options ([79f91e0](https://github.com/revanced/revanced-patcher/commit/79f91e0e5a6d99828f30aae55339ce0d897394c7))
## [4.1.1](https://github.com/revanced/revanced-patcher/compare/v4.1.0...v4.1.1) (2022-09-07)
### Bug Fixes
* handle private companion objects ([ad3d332](https://github.com/revanced/revanced-patcher/commit/ad3d332e27d07e9d074bbaaf51af7eb2f9bfc7d5))
# [4.1.0](https://github.com/revanced/revanced-patcher/compare/v4.0.0...v4.1.0) (2022-09-07)
### Features
* deprecation for patches ([80c2e80](https://github.com/revanced/revanced-patcher/commit/80c2e809251cdb04d2dd3b3bfdbb8844bdfa31fa))
# [4.0.0](https://github.com/revanced/revanced-patcher/compare/v3.5.1...v4.0.0) (2022-09-07)
### Code Refactoring
* Improve Patch Options ([6b909c1](https://github.com/revanced/revanced-patcher/commit/6b909c1ee6b8c2ea08bbca059df755e2e5f31656))
### BREAKING CHANGES
* Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
## [3.5.1](https://github.com/revanced/revanced-patcher/compare/v3.5.0...v3.5.1) (2022-09-06)
### Bug Fixes
* add tests for PathOption ([d6308e1](https://github.com/revanced/revanced-patcher/commit/d6308e126c6217b098192c51b6e98bc85a8656bd))
* PathOption should be open, not sealed ([a562e47](https://github.com/revanced/revanced-patcher/commit/a562e476c085841efbc7ee98b01d8e6bb18ed757))
* typo in ListOption ([3921648](https://github.com/revanced/revanced-patcher/commit/392164862c83d6e76b2a2113d6f6d59fef0020d1))
### Performance Improvements
* make exception an object ([75d2be8](https://github.com/revanced/revanced-patcher/commit/75d2be88037c9cf5436ab69d92abea575409a865))
# [3.5.0](https://github.com/revanced/revanced-patcher/compare/v3.4.1...v3.5.0) (2022-09-05) # [3.5.0](https://github.com/revanced/revanced-patcher/compare/v3.4.1...v3.5.0) (2022-09-05)

View File

@@ -26,6 +26,7 @@ dependencies {
implementation("app.revanced:multidexlib2:2.5.2.r2") implementation("app.revanced:multidexlib2:2.5.2.r2")
implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT") implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT")
implementation(kotlin("reflect"))
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
} }
@@ -36,6 +37,9 @@ tasks {
events("PASSED", "SKIPPED", "FAILED") events("PASSED", "SKIPPED", "FAILED")
} }
} }
processResources {
expand("projectVersion" to project.version)
}
} }
java { java {

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 3.5.0 version = 4.2.0

View File

@@ -4,7 +4,9 @@ import app.revanced.patcher.data.Data
import app.revanced.patcher.data.PackageMetadata import app.revanced.patcher.data.PackageMetadata
import app.revanced.patcher.data.impl.findIndexed import app.revanced.patcher.data.impl.findIndexed
import app.revanced.patcher.extensions.PatchExtensions.dependencies import app.revanced.patcher.extensions.PatchExtensions.dependencies
import app.revanced.patcher.extensions.PatchExtensions.deprecated
import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.extensions.PatchExtensions.sincePatcherVersion
import app.revanced.patcher.extensions.nullOutputStream import app.revanced.patcher.extensions.nullOutputStream
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
@@ -14,6 +16,7 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.impl.ResourcePatch import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patcher.util.ListBackedSet import app.revanced.patcher.util.ListBackedSet
import app.revanced.patcher.util.VersionReader
import brut.androlib.Androlib import brut.androlib.Androlib
import brut.androlib.meta.UsesFramework import brut.androlib.meta.UsesFramework
import brut.androlib.options.BuildOptions import brut.androlib.options.BuildOptions
@@ -35,7 +38,8 @@ import java.io.Closeable
import java.io.File import java.io.File
import java.nio.file.Files import java.nio.file.Files
val NAMER = BasicDexFileNamer() private val NAMER = BasicDexFileNamer()
private val VERSION = VersionReader.read()
/** /**
* The ReVanced Patcher. * The ReVanced Patcher.
@@ -243,6 +247,15 @@ class Patcher(private val options: PatcherOptions) {
* @param patches [Patch]es The patches to add. * @param patches [Patch]es The patches to add.
*/ */
fun addPatches(patches: Iterable<Class<out Patch<Data>>>) { fun addPatches(patches: Iterable<Class<out Patch<Data>>>) {
for (patch in patches) {
val needsVersion = patch.sincePatcherVersion
if (needsVersion != null && needsVersion > VERSION) {
logger.error("Patch '${patch.patchName}' requires Patcher version $needsVersion or higher")
logger.error("Current Patcher version is $VERSION")
logger.warn("Skipping '${patch.patchName}'!")
continue // TODO: continue or halt/throw?
}
}
data.patches.addAll(patches) data.patches.addAll(patches)
} }
@@ -289,6 +302,11 @@ class Patcher(private val options: PatcherOptions) {
return PatchResultError("'$patchName' is a resource patch, but resource patching is disabled") return PatchResultError("'$patchName' is a resource patch, but resource patching is disabled")
} }
patch.deprecated?.let { (reason, replacement) ->
logger.warn("'$patchName' is deprecated: $reason")
if (replacement != null) logger.warn("Use '${replacement.java.patchName}' instead")
}
// TODO: find a solution for this // TODO: find a solution for this
val data = if (isResourcePatch) { val data = if (isResourcePatch) {
data.resourceData data.resourceData

View File

@@ -0,0 +1,19 @@
package app.revanced.patcher.annotation
import app.revanced.patcher.data.Data
import app.revanced.patcher.patch.Patch
import kotlin.reflect.KClass
/**
* Declares a [Patch] deprecated for removal.
* @param reason The reason why the patch is deprecated.
* @param replacement The replacement for the deprecated patch, if any.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class PatchDeprecated(
val reason: String,
val replacement: KClass<out Patch<Data>> = Patch::class
// Values cannot be nullable in annotations, so this will have to do.
)

View File

@@ -0,0 +1,13 @@
package app.revanced.patcher.annotation
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.Patcher
/**
* Declares a [Patch] deprecated for removal.
* @param version The minimum version of the [Patcher] this [Patch] supports.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class SincePatcher(val version: String)

View File

@@ -1,13 +1,15 @@
package app.revanced.patcher.extensions package app.revanced.patcher.extensions
import app.revanced.patcher.annotation.Compatibility import app.revanced.patcher.annotation.*
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.Data import app.revanced.patcher.data.Data
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchOptions
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.companionObjectInstance
/** /**
* Recursively find a given annotation on a class. * Recursively find a given annotation on a class.
@@ -36,13 +38,27 @@ private fun <T : Annotation> Class<*>.findAnnotationRecursively(
} }
object PatchExtensions { object PatchExtensions {
val Class<out Patch<Data>>.patchName: String val Class<*>.patchName: String
get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version
val Class<out Patch<Data>>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include val Class<out Patch<Data>>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
val Class<out Patch<Data>>.options: PatchOptions?
get() = kotlin.companionObject?.let { cl ->
if (cl.visibility != KVisibility.PUBLIC) return null
kotlin.companionObjectInstance?.let {
(it as? OptionsContainer)?.options
}
}
val Class<out Patch<Data>>.deprecated: Pair<String, KClass<out Patch<Data>>?>?
get() = recursiveAnnotation(PatchDeprecated::class)?.let {
it.reason to it.replacement.let { cl ->
if (cl == Patch::class) null else cl
}
}
val Class<out Patch<Data>>.sincePatcherVersion get() = recursiveAnnotation(SincePatcher::class)?.version
@JvmStatic @JvmStatic
fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean { fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean {

View File

@@ -17,9 +17,18 @@ abstract class Patch<out T : Data> {
* The main function of the [Patch] which the patcher will call. * The main function of the [Patch] which the patcher will call.
*/ */
abstract fun execute(data: @UnsafeVariance T): PatchResult abstract fun execute(data: @UnsafeVariance T): PatchResult
}
abstract class OptionsContainer {
/** /**
* A list of [PatchOption]s. * A list of [PatchOption]s.
* @see PatchOptions
*/ */
open val options = PatchOptions() @Suppress("MemberVisibilityCanBePrivate")
val options = PatchOptions()
protected fun option(opt: PatchOption<*>): PatchOption<*> {
options.register(opt)
return opt
}
} }

View File

@@ -2,6 +2,8 @@
package app.revanced.patcher.patch package app.revanced.patcher.patch
import java.io.File
import java.nio.file.Path
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
class NoSuchOptionException(val option: String) : Exception("No such option: $option") class NoSuchOptionException(val option: String) : Exception("No such option: $option")
@@ -9,20 +11,24 @@ class IllegalValueException(val value: Any?) : Exception("Illegal value: $value"
class InvalidTypeException(val got: String, val expected: String) : class InvalidTypeException(val got: String, val expected: String) :
Exception("Invalid option value type: $got, expected $expected") Exception("Invalid option value type: $got, expected $expected")
class RequirementNotMetException : Exception("null was passed into an option that requires a value") object RequirementNotMetException : Exception("null was passed into an option that requires a value")
/** /**
* A registry for an array of [PatchOption]s. * A registry for an array of [PatchOption]s.
* @param options An array of [PatchOption]s. * @param options An array of [PatchOption]s.
*/ */
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> { class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
private val register = buildMap { private val register = mutableMapOf<String, PatchOption<*>>()
for (option in options) {
if (containsKey(option.key)) { init {
throw IllegalStateException("Multiple options found with the same key") options.forEach { register(it) }
} }
put(option.key, option)
internal fun register(option: PatchOption<*>) {
if (register.containsKey(option.key)) {
throw IllegalStateException("Multiple options found with the same key")
} }
register[option.key] = option
} }
/** /**
@@ -75,9 +81,15 @@ sealed class PatchOption<T>(
val validator: (T?) -> Boolean val validator: (T?) -> Boolean
) { ) {
var value: T? = default var value: T? = default
get() {
if (field == null && required) {
throw RequirementNotMetException
}
return field
}
set(value) { set(value) {
if (value == null && required) { if (value == null && required) {
throw RequirementNotMetException() throw RequirementNotMetException
} }
if (!validator(value)) { if (!validator(value)) {
throw IllegalValueException(value) throw IllegalValueException(value)
@@ -89,7 +101,11 @@ sealed class PatchOption<T>(
* Gets the value of the option. * Gets the value of the option.
* Please note that using the wrong value type results in a runtime error. * Please note that using the wrong value type results in a runtime error.
*/ */
operator fun <T> getValue(thisRef: Nothing?, property: KProperty<*>) = value as T inline operator fun <reified V> getValue(thisRef: Any?, property: KProperty<*>) =
value as? V ?: throw InvalidTypeException(
V::class.java.canonicalName,
value?.let { it::class.java.canonicalName } ?: "null"
)
/** /**
* Gets the value of the option. * Gets the value of the option.
@@ -152,8 +168,8 @@ sealed class PatchOption<T>(
} }
) { ) {
init { init {
if (default !in options) { if (default != null && default !in options) {
throw IllegalStateException("Default option must be an allowed options") throw IllegalStateException("Default option must be an allowed option")
} }
} }
} }
@@ -189,4 +205,36 @@ sealed class PatchOption<T>(
) : ListOption<Int>( ) : ListOption<Int>(
key, default, options, title, description, required, validator key, default, options, title, description, required, validator
) )
}
/**
* A [PatchOption] representing a [Path].
* @see PatchOption
*/
open class PathOption(
key: String,
default: Path?,
title: String,
description: String,
required: Boolean = false,
validator: (Path?) -> Boolean = { true }
) : PatchOption<Path>(
key, default, title, description, required, validator
)
/**
* A [PathOption] of type [File].
* @see PathOption
*/
class FileOption(
key: String,
default: File?,
title: String,
description: String,
required: Boolean = false,
validator: (File?) -> Boolean = { true }
) : PathOption(
key, default?.toPath(), title, description, required, {
validator(it?.toFile())
}
)
}

View File

@@ -0,0 +1,18 @@
package app.revanced.patcher.util
import java.util.*
internal object VersionReader {
@JvmStatic
private val props = Properties().apply {
load(
VersionReader::class.java.getResourceAsStream("/revanced-patcher/version.properties")
?: throw IllegalStateException("Could not load version.properties")
)
}
@JvmStatic
fun read(): String {
return props.getProperty("version") ?: throw IllegalStateException("Version not found")
}
}

View File

@@ -0,0 +1 @@
version=${projectVersion}

View File

@@ -3,10 +3,11 @@ package app.revanced.patcher.patch
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import java.io.File
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
internal class PatchOptionsTest { internal class PatchOptionsTest {
private val options = ExampleBytecodePatch().options private val options = ExampleBytecodePatch.options
@Test @Test
fun `should not throw an exception`() { fun `should not throw an exception`() {
@@ -15,21 +16,28 @@ internal class PatchOptionsTest {
is PatchOption.StringOption -> { is PatchOption.StringOption -> {
option.value = "Hello World" option.value = "Hello World"
} }
is PatchOption.BooleanOption -> { is PatchOption.BooleanOption -> {
option.value = false option.value = false
} }
is PatchOption.StringListOption -> { is PatchOption.StringListOption -> {
option.value = option.options.first() option.value = option.options.first()
for (choice in option.options) { for (choice in option.options) {
println(choice) println(choice)
} }
} }
is PatchOption.IntListOption -> { is PatchOption.IntListOption -> {
option.value = option.options.first() option.value = option.options.first()
for (choice in option.options) { for (choice in option.options) {
println(choice) println(choice)
} }
} }
is PatchOption.PathOption -> {
option.value = File("test.txt").toPath()
}
} }
} }
val option = options["key1"] val option = options["key1"]
@@ -77,9 +85,16 @@ internal class PatchOptionsTest {
} }
@Test @Test
fun `should fail because of the requirement is not met`() { fun `should fail because the requirement is not met`() {
assertThrows<RequirementNotMetException> { assertThrows<RequirementNotMetException> {
options.nullify("key1") options.nullify("key1")
} }
} }
@Test
fun `should fail because getting a non-initialized option is illegal`() {
assertThrows<RequirementNotMetException> {
println(options["key6"].value)
}
}
} }

View File

@@ -7,8 +7,8 @@ import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.PatchOption import app.revanced.patcher.patch.PatchOption
import app.revanced.patcher.patch.PatchOptions
import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
@@ -32,6 +32,8 @@ import org.jf.dexlib2.immutable.reference.ImmutableFieldReference
import org.jf.dexlib2.immutable.reference.ImmutableStringReference import org.jf.dexlib2.immutable.reference.ImmutableStringReference
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
import org.jf.dexlib2.util.Preconditions import org.jf.dexlib2.util.Preconditions
import java.io.File
import java.nio.file.Path
@Patch @Patch
@Name("example-bytecode-patch") @Name("example-bytecode-patch")
@@ -46,6 +48,10 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
// Get the resolved method by its fingerprint from the resolver cache // Get the resolved method by its fingerprint from the resolver cache
val result = ExampleFingerprint.result!! val result = ExampleFingerprint.result!!
// Patch options
println(key1)
key2 = false
// Get the implementation for the resolved method // Get the implementation for the resolved method
val method = result.mutableMethod val method = result.mutableMethod
val implementation = method.implementation!! val implementation = method.implementation!!
@@ -164,18 +170,36 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
) )
} }
override val options = PatchOptions( companion object : OptionsContainer() {
PatchOption.StringOption( private var key1: String by option(
"key1", "default", "title", "description", true PatchOption.StringOption(
), "key1", "default", "title", "description", true
PatchOption.BooleanOption( )
"key2", true, "title", "description" // required defaults to false )
), private var key2: Boolean by option(
PatchOption.StringListOption( PatchOption.BooleanOption(
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description" "key2", true, "title", "description" // required defaults to false
), )
PatchOption.IntListOption( )
"key4", 1, listOf(1, 2, 3), "title", "description" private var key3: String by option(
), PatchOption.StringListOption(
) "key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
)
)
private var key4: Int by option(
PatchOption.IntListOption(
"key4", 1, listOf(1, 2, 3), "title", "description"
)
)
private var key5: Path by option(
PatchOption.PathOption(
"key5", File("test.txt").toPath(), "title", "description"
)
)
private var key6: String by option(
PatchOption.StringOption(
"key6", null, "title", "description", true
)
)
}
} }

View File

@@ -0,0 +1,20 @@
package app.revanced.patcher.util
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
internal class VersionReaderTest {
@Test
fun read() {
val version = VersionReader.read()
assertNotNull(version)
assertTrue(version.isNotEmpty())
val parts = version.split(".")
assertEquals(3, parts.size)
parts.forEach {
assertTrue(it.toInt() >= 0)
}
println(version)
}
}