diff --git a/core/java/android/content/res/FontScaleConverter.java b/core/java/android/content/res/FontScaleConverter.java index c7fdb16682e3d..457225d26610c 100644 --- a/core/java/android/content/res/FontScaleConverter.java +++ b/core/java/android/content/res/FontScaleConverter.java @@ -36,11 +36,6 @@ import java.util.Arrays; * @hide */ public class FontScaleConverter { - /** - * How close the given SP should be to a canonical SP in the array before they are considered - * the same for lookup purposes. - */ - private static final float THRESHOLD_FOR_MATCHING_SP = 0.02f; @VisibleForTesting final float[] mFromSpValues; @@ -78,10 +73,11 @@ public class FontScaleConverter { public float convertSpToDp(float sp) { final float spPositive = Math.abs(sp); // TODO(b/247861374): find a match at a higher index? - final int spRounded = Math.round(spPositive); final float sign = Math.signum(sp); - final int index = Arrays.binarySearch(mFromSpValues, spRounded); - if (index >= 0 && Math.abs(spRounded - spPositive) < THRESHOLD_FOR_MATCHING_SP) { + // We search for exact matches only, even if it's just a little off. The interpolation will + // handle any non-exact matches. + final int index = Arrays.binarySearch(mFromSpValues, spPositive); + if (index >= 0) { // exact match, return the matching dp return sign * mToDpValues[index]; } else { diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt index cfca0375bb962..625c318d9efd7 100644 --- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt @@ -18,8 +18,12 @@ package android.content.res import androidx.core.util.forEach import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import kotlin.math.ceil +import kotlin.math.floor import org.junit.Test import org.junit.runner.RunWith @@ -72,7 +76,70 @@ class FontScaleConverterFactoryTest { } } + @LargeTest + @Test + fun allFeasibleScalesAndConversionsDoNotCrash() { + generateSequenceOfFractions(-10000f..10000f, step = 0.01f) + .mapNotNull{ FontScaleConverterFactory.forScale(it) } + .flatMap{ table -> + generateSequenceOfFractions(-10000f..10000f, step = 0.01f) + .map{ Pair(table, it) } + } + .forEach { (table, sp) -> + try { + assertWithMessage( + "convertSpToDp(%s) on table: %s", + sp.toString(), + table.toString() + ) + .that(table.convertSpToDp(sp)) + .isFinite() + } catch (e: Exception) { + throw AssertionError("Exception during convertSpToDp($sp) on table: $table", e) + } + } + } + + @Test + fun testGenerateSequenceOfFractions() { + val fractions = generateSequenceOfFractions(-1000f..1000f, step = 0.1f) + .toList() + fractions.forEach { + assertThat(it).isAtLeast(-1000f) + assertThat(it).isAtMost(1000f) + } + + assertThat(fractions).isInStrictOrder() + assertThat(fractions).hasSize(1000 * 2 * 10 + 1) // Don't forget the 0 in the middle! + + assertThat(fractions).contains(100f) + assertThat(fractions).contains(500.1f) + assertThat(fractions).contains(500.2f) + assertThat(fractions).contains(0.2f) + assertThat(fractions).contains(0f) + assertThat(fractions).contains(-10f) + assertThat(fractions).contains(-10f) + assertThat(fractions).contains(-10.3f) + + assertThat(fractions).doesNotContain(-10.31f) + assertThat(fractions).doesNotContain(0.35f) + assertThat(fractions).doesNotContain(0.31f) + assertThat(fractions).doesNotContain(-.35f) + } + companion object { private const val CONVERSION_TOLERANCE = 0.05f } } + +fun generateSequenceOfFractions( + range: ClosedFloatingPointRange, + step: Float +): Sequence { + val multiplier = 1f / step + val start = floor(range.start * multiplier).toInt() + val endInclusive = ceil(range.endInclusive * multiplier).toInt() + return generateSequence(start) { it + 1 } + .takeWhile { it <= endInclusive } + .map{ it.toFloat() / multiplier } +}