diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd61adbf6..36e201c83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/**.gradle', '**/**.gradle.kts', '**/gradle/wrapper/gradle-wrapper.properties', '**/buildSrc/src/main/kotlin/**.kt') }} - name: Unit tests - run: ./gradlew testDebugUnitTest + run: ./gradlew test instrumentation-tests: name: Instrumentation tests diff --git a/base/src/main/kotlin/io/goooler/demoapp/base/network/BaseRetrofitHelper.kt b/base/src/main/kotlin/io/goooler/demoapp/base/network/BaseRetrofitHelper.kt index f76f5b48a..5908d625c 100644 --- a/base/src/main/kotlin/io/goooler/demoapp/base/network/BaseRetrofitHelper.kt +++ b/base/src/main/kotlin/io/goooler/demoapp/base/network/BaseRetrofitHelper.kt @@ -10,10 +10,10 @@ import okhttp3.OkHttpClient import retrofit2.Converter import retrofit2.Retrofit -@Suppress("unused") abstract class BaseRetrofitHelper { - private val retrofit by lazy { + @PublishedApi + internal val retrofit by lazy { Retrofit.Builder() .baseUrl(baseUrl) .client(buildOkHttpClient()) @@ -32,9 +32,7 @@ abstract class BaseRetrofitHelper { } } - fun create(service: Class): T = retrofit.create(service) - - inline fun create(): T = create(T::class.java) + inline fun create(): T = retrofit.create(T::class.java) protected abstract fun OkHttpClient.Builder.addInterceptors(): OkHttpClient.Builder diff --git a/buildSrc/src/main/kotlin/Extensions.kt b/buildSrc/src/main/kotlin/Extensions.kt index 52827afd3..a7dbe6e52 100644 --- a/buildSrc/src/main/kotlin/Extensions.kt +++ b/buildSrc/src/main/kotlin/Extensions.kt @@ -57,6 +57,9 @@ fun DependencyHandler.androidTestImplementations(vararg names: Any): Array = config("testImplementation", *names) +fun DependencyHandler.kaptTests(vararg names: Any): Array = + config("kaptTest", *names) + fun PluginAware.applyPlugins(vararg names: String) { apply { names.forEach(::plugin) } } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 3e84b47d6..3a63a4d08 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -29,4 +29,6 @@ dependencies { implementations(*Libs.coil) debugImplementations(Libs.chuckerDebug) releaseImplementations(Libs.chuckerRelease) + + kaptTests(Libs.moshiCompiler) } diff --git a/common/src/main/kotlin/io/goooler/demoapp/common/util/JsonUtil.kt b/common/src/main/kotlin/io/goooler/demoapp/common/util/JsonUtil.kt index e57701f9e..5634601ca 100644 --- a/common/src/main/kotlin/io/goooler/demoapp/common/util/JsonUtil.kt +++ b/common/src/main/kotlin/io/goooler/demoapp/common/util/JsonUtil.kt @@ -4,47 +4,43 @@ package io.goooler.demoapp.common.util import com.squareup.moshi.Moshi import com.squareup.moshi.Types -import java.lang.reflect.Type +import org.intellij.lang.annotations.Language object JsonUtil { + @PublishedApi internal val moshi: Moshi = Moshi.Builder().build() - fun fromJson(json: String, clazz: Class): T? = try { - moshi.adapter(clazz).fromJson(json) + inline fun fromJson(@Language("JSON") string: String): T? = try { + moshi.adapter(T::class.java).fromJson(string) } catch (e: Exception) { e.printStackTrace() null } - fun fromJson(json: String, typeOfT: Type): T? = try { - moshi.adapter(typeOfT).fromJson(json) + inline fun fromJson( + @Language("JSON") string: String, + rawType: Class<*>, + vararg typeArguments: Class<*> + ): T? = try { + moshi.adapter(Types.newParameterizedType(rawType, *typeArguments)).fromJson(string) } catch (e: Exception) { e.printStackTrace() null } - fun fromJson(json: String, rawType: Class<*>, vararg typeArguments: Class<*>): T? = try { - fromJson(json, Types.newParameterizedType(rawType, *typeArguments)) + @Language("JSON") + inline fun toJson(value: T?): String? = try { + moshi.adapter(T::class.java).toJson(value) } catch (e: Exception) { e.printStackTrace() null } - - fun toJson(o: T?, clazz: Class): String? = try { - moshi.adapter(clazz).toJson(o) - } catch (e: Exception) { - e.printStackTrace() - null - } - - inline fun toJson(o: T?): String? = toJson(o, T::class.java) } -inline fun String.fromJson(): T? = JsonUtil.fromJson(this, T::class.java) - -inline fun String.fromJson(typeOfT: Type): T? = JsonUtil.fromJson(this, typeOfT) +inline fun String.fromJson(): T? = JsonUtil.fromJson(this) inline fun String.fromJson(rawType: Class<*>, vararg typeArguments: Class<*>): T? = JsonUtil.fromJson(this, rawType, *typeArguments) +@Language("JSON") fun Any?.toJson(): String? = JsonUtil.toJson(this) diff --git a/common/src/test/kotlin/io/goooler/demoapp/common/JsonUtilTest.kt b/common/src/test/kotlin/io/goooler/demoapp/common/JsonUtilTest.kt new file mode 100644 index 000000000..0872bcf0a --- /dev/null +++ b/common/src/test/kotlin/io/goooler/demoapp/common/JsonUtilTest.kt @@ -0,0 +1,85 @@ +package io.goooler.demoapp.common + +import com.squareup.moshi.JsonClass +import io.goooler.demoapp.base.util.secondOrNull +import io.goooler.demoapp.common.util.JsonUtil +import io.goooler.demoapp.common.util.fromJson +import io.goooler.demoapp.common.util.toJson +import org.intellij.lang.annotations.Language +import org.junit.Assert.assertTrue +import org.junit.Test + +class JsonUtilTest { + @Test + fun `JsonUtil fromJson(String)`() { + assertTrue(JsonUtil.fromJson(firstStr) == firstBean) + assertTrue(JsonUtil.fromJson(secondStr) == secondBean) + } + + @Test + fun `JsonUtil fromJson(String, Class, Class)`() { + val list: List = JsonUtil.fromJson(strArray, List::class.java, Repo::class.java) + ?: throw Exception("Parse json error") + assertTrue(list.firstOrNull() == firstBean) + assertTrue(list.secondOrNull() == secondBean) + } + + @Test + fun `JsonUtil toJson(T)`() { + assertTrue(JsonUtil.toJson(firstBean) == firstStr) + assertTrue(JsonUtil.toJson(secondBean) == secondStr) + } + + @Test + fun `String fromJson()`() { + assertTrue(firstStr.fromJson() == firstBean) + assertTrue(secondStr.fromJson() == secondBean) + } + + @Test + fun `String fromJson(Class, Class)`() { + val list: List = strArray.fromJson(List::class.java, Repo::class.java) + ?: throw Exception("Parse json error") + assertTrue(list.first() == firstBean) + assertTrue(list[1] == secondBean) + } + + @Test + fun `Any toJson(T)`() { + assertTrue(firstBean.toJson() == firstStr) + assertTrue(secondBean.toJson() == secondStr) + } + + @JsonClass(generateAdapter = true) + internal data class Repo(val id: Long, val name: String, val owner: Owner) { + @JsonClass(generateAdapter = true) + data class Owner(val login: String) + + override fun equals(other: Any?): Boolean = if (other is Repo) { + this.id == other.id && this.name == other.name && this.owner.login == other.owner.login + } else { + false + } + + override fun hashCode(): Int = id.hashCode() + } + + companion object { + @Language("JSON") + private val firstStr = """ + {"id":126987864,"name":"1024_hosts","owner":{"login":"Goooler"}} + """.trimIndent() + + @Language("JSON") + private val secondStr = """ + {"id":374913489,"name":"AndroidUiDemo","owner":{"login":"Goooler"}} + """.trimIndent() + + // https://api.github.com/users/goooler/repos?&page=1&per_page=2 + @Language("JSON") + private val strArray = "[$firstStr,$secondStr]" + + private val firstBean = Repo(126987864, "1024_hosts", Repo.Owner("Goooler")) + private val secondBean = Repo(374913489, "AndroidUiDemo", Repo.Owner("Goooler")) + } +}