mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
Support parsing multiplatform tests
commit_hash:30c0f59f613adf7914f390c20f751136e7b34b75
This commit is contained in:
@@ -22,6 +22,9 @@ dependencies {
|
||||
|
||||
api(libs.androidx.core)
|
||||
|
||||
testImplementation(project(":test-utils"))
|
||||
|
||||
testImplementation(libs.json)
|
||||
testImplementation(libs.kotlin.reflect)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -774,7 +774,7 @@ public class JsonPropertyParser {
|
||||
@Nullable final List<V> list,
|
||||
@NonNull final Function1<V, R> converter
|
||||
) {
|
||||
if (list != null && !list.isEmpty()) {
|
||||
if (list != null) {
|
||||
int length = list.size();
|
||||
JSONArray array = new JSONArray();
|
||||
for (int i = 0; i < length; i++) {
|
||||
@@ -796,7 +796,7 @@ public class JsonPropertyParser {
|
||||
@Nullable final List<V> list,
|
||||
@NonNull final Lazy<Serializer<JSONObject, V>> serializer
|
||||
) {
|
||||
if (list != null && !list.isEmpty()) {
|
||||
if (list != null) {
|
||||
int length = list.size();
|
||||
JSONArray array = new JSONArray();
|
||||
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package com.yandex.div.internal.parser
|
||||
|
||||
import com.yandex.div.data.DivParsingEnvironment
|
||||
import com.yandex.div.internal.util.forEach
|
||||
import com.yandex.div.json.ParsingErrorLogger
|
||||
import com.yandex.div.test.crossplatform.ParsingResult
|
||||
import com.yandex.div.test.crossplatform.ParsingUtils.parseFiles
|
||||
import com.yandex.div.test.crossplatform.isForAndroid
|
||||
import com.yandex.div2.DivData
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class ParsingMultiplatformTest(testCaseParsingResult: ParsingResult<ParsingTestCase>) {
|
||||
|
||||
private val testCase = testCaseParsingResult.getOrThrow()
|
||||
private val errors = mutableListOf<Throwable>()
|
||||
|
||||
@Test
|
||||
fun run() {
|
||||
val logger = ParsingErrorLogger { errors += it }
|
||||
val env = DivParsingEnvironment(logger)
|
||||
|
||||
val divData = runCatching {
|
||||
testCase.templates?.let { env.parseTemplates(it) }
|
||||
DivData(env, testCase.card)
|
||||
}.getOrNull()
|
||||
|
||||
assertCardEquals(testCase.expectedResult.card, divData?.writeToJSON())
|
||||
Assert.assertEquals("Unexpected error count:", testCase.expectedResult.errorCount, errors.size)
|
||||
}
|
||||
|
||||
private fun assertCardEquals(expected: JSONObject?, actual: JSONObject?) {
|
||||
expected ?: return Assert.assertNull(actual)
|
||||
actual ?: return Assert.fail(
|
||||
"Failed to parse DivData. Errors: ${errors.joinToString(", ") { it.message ?: "" }}"
|
||||
)
|
||||
|
||||
runCatching {
|
||||
expected.compareValues("", actual)
|
||||
}.onFailure {
|
||||
Assert.fail(it.message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun JSONObject.compareValues(parentPath: String, actual: JSONObject): Boolean {
|
||||
forEach { key, expectedValue: Any? ->
|
||||
val actualValue = actual.opt(key)
|
||||
val path = parentPath.takeIf { it.isNotEmpty() }?.let { "$it/$key" } ?: key
|
||||
if (!expectedValue.compare(path, actualValue)) {
|
||||
throwError(path, expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun Any?.compare(path: String, actual: Any?): Boolean {
|
||||
return when (this) {
|
||||
JSONObject.NULL -> actual == null || actual == this
|
||||
is JSONObject -> (actual as? JSONObject)?.let { compareValues(path, it) } ?: false
|
||||
is JSONArray -> (actual as? JSONArray)?.let { compareValues(path, it)} ?: false
|
||||
is Int -> actual == this || (actual as? Long)?.toInt() == this
|
||||
else -> actual == this
|
||||
}
|
||||
}
|
||||
|
||||
private fun JSONArray.compareValues(parentPath: String, actual: JSONArray): Boolean {
|
||||
forEach { i, expectedValue: Any? ->
|
||||
val actualValue = actual.opt(i)
|
||||
val path = parentPath.takeIf { it.isNotEmpty() }?.let { "$it/$i" } ?: "$i"
|
||||
if (!expectedValue.compare(path, actualValue)) {
|
||||
throwError(path, expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun throwError(path: String, expected: Any?, actual: Any?): Nothing =
|
||||
throw Exception("DivData comparison failed for path '$path', expected='$expected', actual='$actual'")
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
fun cases(): List<ParsingResult<ParsingTestCase>> {
|
||||
return parseFiles("parsing_test_data") { file, jsonString ->
|
||||
val json = JSONObject(jsonString)
|
||||
if (!json.isForAndroid) return@parseFiles emptyList()
|
||||
|
||||
val result = try {
|
||||
val testCase = ParsingTestCase.from(file.name, json)
|
||||
ParsingResult.Success(testCase)
|
||||
} catch (e: Exception) {
|
||||
ParsingResult.Error(fileName = file.name, error = e)
|
||||
}
|
||||
listOf(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.yandex.div.internal.parser
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
class ParsingTestCase(
|
||||
val name: String,
|
||||
val templates: JSONObject?,
|
||||
val card: JSONObject,
|
||||
val expectedResult: ExpectedResult,
|
||||
) {
|
||||
|
||||
class ExpectedResult(
|
||||
val errorCount: Int?,
|
||||
val card: JSONObject?,
|
||||
)
|
||||
|
||||
override fun toString() = name
|
||||
|
||||
companion object {
|
||||
@Throws(JSONException::class)
|
||||
fun from(name: String, json: JSONObject): ParsingTestCase {
|
||||
val expectedResult = json.getJSONObject("expected")
|
||||
return ParsingTestCase(
|
||||
name = name,
|
||||
templates = json.optJSONObject("templates"),
|
||||
card = json.getJSONObject("card"),
|
||||
expectedResult = ExpectedResult(
|
||||
errorCount = expectedResult.optInt("error_count"),
|
||||
card = expectedResult.optJSONObject("card"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ data class ExpressionTestCase(
|
||||
val expression: String,
|
||||
val variables: List<JSONObject>,
|
||||
val functions: List<JSONObject>,
|
||||
val platform: List<String>,
|
||||
val expectedType: String,
|
||||
val expectedValue: Any,
|
||||
val expectedWarnings: List<String>,
|
||||
|
||||
-2
@@ -10,7 +10,6 @@ import com.yandex.div.json.ParsingErrorLogger
|
||||
import com.yandex.div.test.crossplatform.ParsingResult
|
||||
import com.yandex.div.test.crossplatform.isForAndroid
|
||||
import com.yandex.div.test.crossplatform.parseDateTime
|
||||
import com.yandex.div.test.crossplatform.platforms
|
||||
import com.yandex.div.test.crossplatform.toObjectList
|
||||
import com.yandex.div2.DivData
|
||||
import com.yandex.div2.DivEvaluableType
|
||||
@@ -64,7 +63,6 @@ object ExpressionTestCaseUtils {
|
||||
json.getString(CASE_EXPRESSION_VALUE_FIELD),
|
||||
json.optJSONArray(CASE_VARIABLES_FIELD).toObjectList(),
|
||||
json.optJSONArray(CASE_FUNCTIONS_FIELD).toObjectList(),
|
||||
json.platforms,
|
||||
json.getJSONObject(CASE_EXPECTED_VALUE_FIELD).type,
|
||||
json.getJSONObject(CASE_EXPECTED_VALUE_FIELD).getValue(),
|
||||
json.optJSONArray(CASE_EXPECTED_WARNINGS_FIELD)?.map { it as String } ?: emptyList(),
|
||||
|
||||
+3
-3
@@ -55,11 +55,11 @@ object ParsingUtils {
|
||||
}
|
||||
}
|
||||
|
||||
val JSONObject.platforms: List<String>
|
||||
get() = getJSONArray("platforms").map { it as String }
|
||||
val JSONObject.platforms: List<String>?
|
||||
get() = optJSONArray("platforms")?.map { it as String }
|
||||
|
||||
val JSONObject.isForAndroid: Boolean
|
||||
get() = platforms.contains("android")
|
||||
get() = platforms?.contains("android") ?: true
|
||||
|
||||
fun JSONArray?.toObjectList(): List<JSONObject> {
|
||||
if (this == null) {
|
||||
|
||||
Reference in New Issue
Block a user