<lambda>null1 package com.airbnb.lottie.snapshots.tests
2
3 import android.util.Log
4 import com.airbnb.lottie.LottieCompositionFactory
5 import com.airbnb.lottie.snapshots.BuildConfig
6 import com.airbnb.lottie.snapshots.SnapshotTestCase
7 import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
8 import com.airbnb.lottie.snapshots.snapshotComposition
9 import com.airbnb.lottie.snapshots.utils.await
10 import com.airbnb.lottie.snapshots.utils.retry
11 import com.amazonaws.auth.BasicAWSCredentials
12 import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility
13 import com.amazonaws.services.s3.AmazonS3Client
14 import com.amazonaws.services.s3.model.ListObjectsV2Request
15 import com.amazonaws.services.s3.model.S3ObjectSummary
16 import kotlinx.coroutines.CoroutineScope
17 import kotlinx.coroutines.Dispatchers
18 import kotlinx.coroutines.channels.Channel
19 import kotlinx.coroutines.channels.ReceiveChannel
20 import kotlinx.coroutines.channels.produce
21 import kotlinx.coroutines.coroutineScope
22 import kotlinx.coroutines.launch
23 import java.io.File
24 import java.io.FileInputStream
25 import java.util.concurrent.atomic.AtomicInteger
26 import java.util.zip.ZipInputStream
27
28 /**
29 * TODO:
30 * prod-com.eharmony-lottie-loader-data
31 */
32 class ProdAnimationsTestCase : SnapshotTestCase {
33 private val filesChannel = Channel<File>(capacity = 2_048)
34
35 override suspend fun SnapshotTestCaseContext.run() = coroutineScope {
36 val compositionsChannel = parseCompositions(filesChannel)
37 val num = AtomicInteger()
38 repeat(3) {
39 launch {
40 for ((name, composition) in compositionsChannel) {
41 Log.d(TAG, "Snapshot ${num.incrementAndGet()}")
42 snapshotComposition(name, composition = composition)
43 }
44 }
45 }
46 }
47
48 @Suppress("BlockingMethodInNonBlockingContext")
49 fun CoroutineScope.parseCompositions(files: ReceiveChannel<File>) = produce(
50 context = Dispatchers.IO,
51 capacity = 50,
52 ) {
53 val num = AtomicInteger()
54 for (file in files) {
55 val result = if (file.name.endsWith("zip")) LottieCompositionFactory.fromZipStreamSync(ZipInputStream(FileInputStream(file)), null)
56 else LottieCompositionFactory.fromJsonInputStreamSync(FileInputStream(file), null)
57 val composition = result.value ?: throw IllegalStateException("Unable to parse ${file.nameWithoutExtension}", result.exception)
58 Log.d(TAG, "Parse ${num.incrementAndGet()}")
59 send("prod-${file.nameWithoutExtension}" to composition)
60 }
61 }
62
63 suspend fun SnapshotTestCaseContext.downloadAnimations() = coroutineScope {
64 val transferUtility = TransferUtility.builder()
65 .context(context)
66 .s3Client(AmazonS3Client(BasicAWSCredentials(BuildConfig.S3AccessKey, BuildConfig.S3SecretKey)))
67 .defaultBucket("lottie-prod-animations")
68 .build()
69
70 val num = AtomicInteger()
71 coroutineScope {
72 val animations = fetchAllObjects("lottie-prod-animations")
73 animations
74 .chunked(animations.size / 4)
75 .forEach { animationsChunk ->
76 launch(Dispatchers.IO) {
77 for (animation in animationsChunk) {
78 val file = File(context.cacheDir, animation.key)
79 file.deleteOnExit()
80 retry { _, _ ->
81 transferUtility.download(animation.key, file).await()
82 }
83 Log.d(TAG, "Downloaded ${num.incrementAndGet()}")
84 filesChannel.send(file)
85 }
86 }
87 }
88 }
89 Log.d(TAG, "Finished downloading")
90 filesChannel.close()
91 }
92
93 private fun fetchAllObjects(bucket: String): List<S3ObjectSummary> {
94 val allObjects = mutableListOf<S3ObjectSummary>()
95 val s3Client = AmazonS3Client(BasicAWSCredentials(BuildConfig.S3AccessKey, BuildConfig.S3SecretKey))
96 var request = ListObjectsV2Request().apply {
97 bucketName = bucket
98 }
99 var result = s3Client.listObjectsV2(request)
100 allObjects.addAll(result.objectSummaries)
101 var startAfter = result.objectSummaries.lastOrNull()?.key
102 while (startAfter != null) {
103 request = ListObjectsV2Request().apply {
104 bucketName = bucket
105 this.startAfter = startAfter
106 }
107 result = s3Client.listObjectsV2(request)
108 allObjects.addAll(result.objectSummaries)
109 startAfter = result.objectSummaries.lastOrNull()?.key
110 }
111 return allObjects
112 }
113
114 companion object {
115 private const val TAG = "ProdAnimationsTest"
116 }
117 }