Skip to content

Commit 53a744b

Browse files
authored
Merge branch 'master' into master
2 parents 238a548 + c06a85d commit 53a744b

File tree

6 files changed

+142
-218
lines changed

6 files changed

+142
-218
lines changed

integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideImageCustomDrawableTransformationTest.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ class GlideImageCustomDrawableTransformationTest(
3434
// though it's only used by Parameters to create the test name.
3535
@Suppress("unused") private val name: String,
3636
) {
37-
@get:Rule val glideComposeRule = GlideComposeRule()
37+
@get:Rule
38+
val glideComposeRule = GlideComposeRule()
3839

3940
@Test
4041
fun glideImage_nonBitmapDrawable_doesNotThrow() = runTest {
@@ -98,6 +99,7 @@ private open class FakeDrawable : Drawable() {
9899
override fun draw(p0: Canvas) {}
99100
override fun setAlpha(p0: Int) {}
100101
override fun setColorFilter(p0: ColorFilter?) {}
102+
101103
@Deprecated("Deprecated in Java")
102104
override fun getOpacity(): Int = throw UnsupportedOperationException()
103105
}

integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideImageErrorTest.kt

Lines changed: 5 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import org.junit.Test
1818
* assertions about loads that have not yet completed.
1919
*/
2020
@OptIn(ExperimentalGlideComposeApi::class)
21-
@Suppress("DEPRECATION") // Tests for a deprecated method
2221
class GlideImageErrorTest {
2322
private val context: Context = ApplicationProvider.getApplicationContext()
23+
2424
@get:Rule
2525
val glideComposeRule = GlideComposeRule()
2626

@@ -64,7 +64,7 @@ class GlideImageErrorTest {
6464

6565
glideComposeRule
6666
.onNodeWithContentDescription(description)
67-
.assert(expectDisplayedResource(failureResourceId))
67+
.assert(expectDisplayedPainter(context, failureResourceId))
6868
}
6969

7070
@Test
@@ -81,7 +81,7 @@ class GlideImageErrorTest {
8181

8282
glideComposeRule
8383
.onNodeWithContentDescription(description)
84-
.assert(expectDisplayedDrawable(failureDrawable))
84+
.assert(expectDisplayedPainter(failureDrawable))
8585
}
8686

8787
@Test
@@ -98,33 +98,6 @@ class GlideImageErrorTest {
9898
glideComposeRule.onNodeWithContentDescription(description).assert(expectNoDrawable())
9999
}
100100

101-
@Test
102-
fun failureParameter_withComposable_displaysComposable() {
103-
val failureResourceId = android.R.drawable.star_big_off
104-
val description = "test"
105-
glideComposeRule.setContent {
106-
GlideImage(
107-
model = null,
108-
contentDescription = "none",
109-
failure =
110-
placeholder {
111-
// Nesting GlideImage is not really a good idea, but it's convenient for this test
112-
// because
113-
// we can use our helpers to assert on its contents.
114-
GlideImage(
115-
model = null,
116-
contentDescription = description,
117-
failure = placeholder(failureResourceId),
118-
)
119-
}
120-
)
121-
}
122-
123-
glideComposeRule
124-
.onNodeWithContentDescription(description)
125-
.assert(expectDisplayedResource(failureResourceId))
126-
}
127-
128101
@Test
129102
fun failure_setViaFailureParameterWithResourceId_andRequestBuilderTransform_prefersFailureParameter() {
130103
val description = "test"
@@ -141,7 +114,7 @@ class GlideImageErrorTest {
141114

142115
glideComposeRule
143116
.onNodeWithContentDescription(description)
144-
.assert(expectDisplayedResource(failureResourceId))
117+
.assert(expectDisplayedPainter(context, failureResourceId))
145118
}
146119

147120
@Test
@@ -179,7 +152,7 @@ class GlideImageErrorTest {
179152

180153
glideComposeRule
181154
.onNodeWithContentDescription(description)
182-
.assert(expectDisplayedDrawable(failureDrawable))
155+
.assert(expectDisplayedPainter(failureDrawable))
183156
}
184157

185158
@Test
@@ -197,30 +170,4 @@ class GlideImageErrorTest {
197170

198171
glideComposeRule.onNodeWithContentDescription(description).assert(expectNoDrawable())
199172
}
200-
201-
@Test
202-
fun failure_setViaFailureParameterWithComposable_andRequestBuilderTransform_showsComposable() {
203-
val description = "test"
204-
val failureResourceId = android.R.drawable.star_big_off
205-
glideComposeRule.setContent {
206-
GlideImage(
207-
model = null,
208-
contentDescription = "other",
209-
failure =
210-
placeholder {
211-
GlideImage(
212-
model = null,
213-
contentDescription = description,
214-
failure = placeholder(failureResourceId),
215-
)
216-
},
217-
) {
218-
it.error(android.R.drawable.btn_star)
219-
}
220-
}
221-
222-
glideComposeRule
223-
.onNodeWithContentDescription(description)
224-
.assert(expectDisplayedResource(failureResourceId))
225-
}
226173
}

integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideImagePlaceholderTest.kt

Lines changed: 7 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ import org.junit.Test
2020
* [com.bumptech.glide.integration.compose.test.GlideComposeRule] because we want to make assertions
2121
* about loads that have not yet completed.
2222
*/
23-
@Suppress("DEPRECATION") // Tests for a deprecated method
2423
@OptIn(ExperimentalGlideComposeApi::class)
2524
class GlideImagePlaceholderTest {
2625
private val context: Context = ApplicationProvider.getApplicationContext()
26+
2727
@get:Rule(order = 1)
2828
val composeRule = createComposeRule()
29+
2930
@get:Rule(order = 2)
3031
val waitModelLoaderRule = WaitModelLoaderRule()
32+
3133
@get:Rule(order = 3)
3234
val tearDownGlide = TearDownGlide()
3335

@@ -78,7 +80,7 @@ class GlideImagePlaceholderTest {
7880

7981
composeRule
8082
.onNodeWithContentDescription(description)
81-
.assert(expectDisplayedResource(placeholderResourceId))
83+
.assert(expectDisplayedPainter(context, placeholderResourceId))
8284
}
8385

8486
@Test
@@ -96,7 +98,7 @@ class GlideImagePlaceholderTest {
9698

9799
composeRule
98100
.onNodeWithContentDescription(description)
99-
.assert(expectDisplayedDrawable(placeholderDrawable))
101+
.assert(expectDisplayedPainter(placeholderDrawable))
100102
}
101103

102104
@Test
@@ -114,34 +116,6 @@ class GlideImagePlaceholderTest {
114116
composeRule.onNodeWithContentDescription(description).assert(expectNoDrawable())
115117
}
116118

117-
@Test
118-
fun loadingParameter_withComposable_displaysComposable() {
119-
val waitModel = waitModelLoaderRule.waitOn(android.R.drawable.star_big_on)
120-
val placeholderResourceId = android.R.drawable.star_big_off
121-
val description = "test"
122-
composeRule.setContent {
123-
GlideImage(
124-
model = waitModel,
125-
contentDescription = "none",
126-
loading =
127-
placeholder {
128-
// Nesting GlideImage is not really a good idea, but it's convenient for this test
129-
// because
130-
// we can use our helpers to assert on its contents.
131-
GlideImage(
132-
model = waitModel,
133-
contentDescription = description,
134-
loading = placeholder(placeholderResourceId),
135-
)
136-
}
137-
)
138-
}
139-
140-
composeRule
141-
.onNodeWithContentDescription(description)
142-
.assert(expectDisplayedResource(placeholderResourceId))
143-
}
144-
145119
@Test
146120
fun loading_setViaLoadingParameterWithResourceId_andRequestBuilderTransform_prefersLoadingParameter() {
147121
val description = "test"
@@ -159,7 +133,7 @@ class GlideImagePlaceholderTest {
159133

160134
composeRule
161135
.onNodeWithContentDescription(description)
162-
.assert(expectDisplayedResource(placeholderResourceId))
136+
.assert(expectDisplayedPainter(context, placeholderResourceId))
163137
}
164138

165139
@Test
@@ -179,7 +153,7 @@ class GlideImagePlaceholderTest {
179153

180154
composeRule
181155
.onNodeWithContentDescription(description)
182-
.assert(expectDisplayedDrawable(placeholderDrawable))
156+
.assert(expectDisplayedPainter(placeholderDrawable))
183157
}
184158

185159
@Test
@@ -219,31 +193,4 @@ class GlideImagePlaceholderTest {
219193

220194
composeRule.onNodeWithContentDescription(description).assert(expectNoDrawable())
221195
}
222-
223-
@Test
224-
fun loading_setViaLoadingParameterWithComposable_andRequestBuilderTransform_showsComposable() {
225-
val description = "test"
226-
val waitModel = waitModelLoaderRule.waitOn(android.R.drawable.star_big_on)
227-
val placeholderResourceId = android.R.drawable.star_big_off
228-
composeRule.setContent {
229-
GlideImage(
230-
model = waitModel,
231-
contentDescription = "other",
232-
loading =
233-
placeholder {
234-
GlideImage(
235-
model = waitModel,
236-
contentDescription = description,
237-
loading = placeholder(placeholderResourceId),
238-
)
239-
},
240-
) {
241-
it.placeholder(android.R.drawable.btn_star)
242-
}
243-
}
244-
245-
composeRule
246-
.onNodeWithContentDescription(description)
247-
.assert(expectDisplayedResource(placeholderResourceId))
248-
}
249196
}

integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/test/expectations.kt

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
@file:OptIn(InternalGlideApi::class)
2-
31
package com.bumptech.glide.integration.compose.test
42

53
import android.content.Context
64
import android.content.res.Resources
75
import android.graphics.Bitmap
8-
import android.graphics.drawable.Animatable
96
import android.graphics.drawable.BitmapDrawable
107
import android.graphics.drawable.Drawable
118
import android.util.TypedValue
12-
import androidx.compose.runtime.MutableState
9+
import androidx.annotation.DrawableRes
10+
import androidx.compose.ui.graphics.ImageBitmap
11+
import androidx.compose.ui.graphics.asImageBitmap
12+
import androidx.compose.ui.graphics.painter.BitmapPainter
1313
import androidx.compose.ui.graphics.painter.Painter
14+
import androidx.compose.ui.res.imageResource
1415
import androidx.compose.ui.semantics.SemanticsPropertyKey
1516
import androidx.compose.ui.test.SemanticsMatcher
1617
import androidx.test.core.app.ApplicationProvider
@@ -30,26 +31,76 @@ fun Int.dpToPixels() =
3031
)
3132
.roundToInt()
3233

34+
@OptIn(InternalGlideApi::class)
3335
fun Int.bitmapSize() = context().resources.getDrawable(this, context().theme).size()
3436

37+
@OptIn(InternalGlideApi::class)
3538
fun Drawable.size() = (this as BitmapDrawable).bitmap.let { Size(it.width, it.height) }
3639

3740
fun expectDisplayedResource(resourceId: Int) =
3841
expectDisplayedDrawable(context().getDrawable(resourceId))
3942

4043
fun Drawable?.bitmapOrThrow(): Bitmap? = if (this == null) null else (this as BitmapDrawable).bitmap
4144

45+
@OptIn(InternalGlideApi::class)
4246
fun expectDisplayedDrawableSize(expectedSize: Size): SemanticsMatcher =
4347
expectDisplayedDrawable(expectedSize) { it?.size() }
4448

4549
fun expectDisplayedDrawable(expectedValue: Drawable?): SemanticsMatcher =
4650
expectDisplayedDrawable(expectedValue.bitmapOrThrow(), ::compareBitmaps) { it.bitmapOrThrow() }
4751

52+
4853
fun expectDisplayedPainter(expectedValue: Painter?): SemanticsMatcher =
4954
expectStateValue(
50-
DisplayedPainterKey, expectedValue, {first, second -> first == second}, {value -> value}
55+
DisplayedPainterKey, expectedValue, { first, second -> first == second }, { value -> value }
5156
)
5257

58+
// These are hacks. We're relying on the ordering of expected.equals(actual) so that our
59+
// DeepEqualsImageBitmap's equals method will be used. This doesn't implement the equals/hashcode
60+
// contract :/
61+
fun expectDisplayedPainter(context: Context, @DrawableRes resourceId: Int): SemanticsMatcher {
62+
val imageBitmap = DeepEqualsImageBitmap(ImageBitmap.imageResource(context.resources, resourceId))
63+
val painter = BitmapPainter(imageBitmap)
64+
return expectDisplayedPainter(painter)
65+
}
66+
67+
fun expectDisplayedPainter(expectedValue: Drawable?): SemanticsMatcher {
68+
val bitmapDrawable = expectedValue as BitmapDrawable
69+
val imageBitmap = DeepEqualsImageBitmap(bitmapDrawable.bitmap.asImageBitmap())
70+
return expectDisplayedPainter(BitmapPainter(imageBitmap))
71+
}
72+
73+
class DeepEqualsImageBitmap(base: ImageBitmap) : ImageBitmap by base {
74+
override fun equals(other: Any?): Boolean {
75+
val compare = other as? ImageBitmap ?: return false
76+
return compare.width == width && compare.height == height
77+
&& compare.hasAlpha == hasAlpha
78+
&& compare.config == config
79+
&& compare.colorSpace == colorSpace
80+
&& equalPixels(this, other)
81+
}
82+
83+
private fun ImageBitmap.readPixels(): IntArray {
84+
val pixels = IntArray(width * height)
85+
readPixels(pixels)
86+
return pixels
87+
}
88+
89+
private fun equalPixels(first: ImageBitmap, second: ImageBitmap): Boolean {
90+
return first.readPixels().contentEquals(second.readPixels())
91+
}
92+
93+
override fun hashCode(): Int {
94+
var result = width.hashCode()
95+
result *= 31 * height.hashCode()
96+
result *= 31 * hasAlpha.hashCode()
97+
result *= 31 * config.hashCode()
98+
result *= 31 * colorSpace.hashCode()
99+
result *= 31 * readPixels().contentHashCode()
100+
return result
101+
}
102+
}
103+
53104
fun expectNoDrawable(): SemanticsMatcher = expectDisplayedDrawable(null)
54105

55106
private fun compareBitmaps(first: Bitmap?, second: Bitmap?): Boolean {
@@ -77,15 +128,16 @@ private fun <ValueT, TransformedValueT> expectStateValue(
77128
): SemanticsMatcher =
78129
SemanticsMatcher("${key.name} = '$expectedValue'") {
79130
val value = transform(it.config.getOrElseNullable(key) { null }?.invoke())
80-
if (!compare(value, expectedValue)) {
131+
if (!compare(expectedValue, value)) {
81132
throw AssertionError("Expected: $expectedValue, but was: $value")
82133
}
83134
true
84135
}
85136

86137
fun expectSameInstance(expectedDrawable: Drawable) =
87138
SemanticsMatcher("${DisplayedDrawableKey.name} = '$expectedDrawable'") {
88-
val actualValue: Drawable? = it.config.getOrElseNullable(DisplayedDrawableKey) { null }?.invoke()
139+
val actualValue: Drawable? =
140+
it.config.getOrElseNullable(DisplayedDrawableKey) { null }?.invoke()
89141
if (actualValue !== expectedDrawable) {
90142
throw AssertionError("Expected: $expectedDrawable, but was: $actualValue")
91143
}

0 commit comments

Comments
 (0)