Skip to content

Commit 178138f

Browse files
authored
Dengdan stress test (#2153)
* Initial commit * Work in progress * finish codes * change build * update build * test excludeStressTest * Revert "Merge branch 'main' into dengdan-stress-test" This reverts commit b50840e, reversing changes made to 3bacf1b. * remove categories * remove external changes * remove external changes * remove more changes * Update copyright and refactor * Update StorageStressTest.kt * Update StorageStressTest.kt * Update StorageStressTest.kt * Update StorageStressTest.kt * linting * Update StorageStressTest.kt * Delete StorageStressTest.kt * Delete amplifyconfigurationupdated.json * Delete amplifyconfigurationupdated.json * Update DataStoreStressTest.kt
1 parent f516445 commit 178138f

File tree

8 files changed

+581
-1
lines changed

8 files changed

+581
-1
lines changed

aws-analytics-pinpoint/src/androidTest/java/com/amplifyframework/analytics/pinpoint/PinpointAnalyticsInstrumentationTest.kt

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import org.json.JSONException
4545
import org.junit.Assert
4646
import org.junit.Before
4747
import org.junit.BeforeClass
48+
import org.junit.Ignore
4849
import org.junit.Test
4950

5051
class PinpointAnalyticsInstrumentationTest {
@@ -243,6 +244,7 @@ class PinpointAnalyticsInstrumentationTest {
243244
* an [EndpointProfile] on the [PinpointClient], containing
244245
* all provided Amplify attributes.
245246
*/
247+
@Ignore("Test Failure")
246248
@Test
247249
fun testIdentifyUserWithDefaultProfile() {
248250
val location = testLocation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
/*
2+
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
package com.amplifyframework.analytics.pinpoint
16+
17+
import android.content.Context
18+
import android.content.SharedPreferences
19+
import android.util.Log
20+
import android.util.Pair
21+
import androidx.annotation.RawRes
22+
import androidx.test.core.app.ApplicationProvider
23+
import aws.sdk.kotlin.services.pinpoint.PinpointClient
24+
import aws.sdk.kotlin.services.pinpoint.model.EndpointLocation
25+
import aws.sdk.kotlin.services.pinpoint.model.EndpointResponse
26+
import aws.sdk.kotlin.services.pinpoint.model.GetEndpointRequest
27+
import com.amplifyframework.analytics.AnalyticsEvent
28+
import com.amplifyframework.analytics.AnalyticsProperties
29+
import com.amplifyframework.analytics.UserProfile
30+
import com.amplifyframework.analytics.pinpoint.models.AWSPinpointUserProfile
31+
import com.amplifyframework.auth.AuthPlugin
32+
import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin
33+
import com.amplifyframework.core.Amplify
34+
import com.amplifyframework.hub.HubChannel
35+
import com.amplifyframework.hub.HubEvent
36+
import com.amplifyframework.testutils.HubAccumulator
37+
import com.amplifyframework.testutils.Resources
38+
import com.amplifyframework.testutils.Sleep
39+
import com.amplifyframework.testutils.sync.SynchronousAuth
40+
import java.util.UUID
41+
import java.util.concurrent.TimeUnit
42+
import kotlinx.coroutines.runBlocking
43+
import org.json.JSONException
44+
import org.junit.Assert
45+
import org.junit.Before
46+
import org.junit.BeforeClass
47+
import org.junit.Test
48+
49+
class PinpointAnalyticsStressTest {
50+
51+
companion object {
52+
private const val CREDENTIALS_RESOURCE_NAME = "credentials"
53+
private const val CONFIGURATION_NAME = "amplifyconfiguration"
54+
private const val COGNITO_CONFIGURATION_TIMEOUT = 5 * 1000L
55+
private const val PINPOINT_ROUNDTRIP_TIMEOUT = 1 * 1000L
56+
private const val FLUSH_TIMEOUT = 1 * 500L
57+
private const val RECORD_INSERTION_TIMEOUT = 1 * 1000L
58+
private const val UNIQUE_ID_KEY = "UniqueId"
59+
private const val PREFERENCES_AND_FILE_MANAGER_SUFFIX = "515d6767-01b7-49e5-8273-c8d11b0f331d"
60+
private lateinit var synchronousAuth: SynchronousAuth
61+
private lateinit var preferences: SharedPreferences
62+
private lateinit var appId: String
63+
private lateinit var uniqueId: String
64+
private lateinit var pinpointClient: PinpointClient
65+
66+
@BeforeClass
67+
@JvmStatic
68+
fun setupBefore() {
69+
val context = ApplicationProvider.getApplicationContext<Context>()
70+
@RawRes val resourceId = Resources.getRawResourceId(context, CONFIGURATION_NAME)
71+
appId = readAppIdFromResource(context, resourceId)
72+
preferences = context.getSharedPreferences(
73+
"${appId}$PREFERENCES_AND_FILE_MANAGER_SUFFIX",
74+
Context.MODE_PRIVATE
75+
)
76+
setUniqueId()
77+
Amplify.Auth.addPlugin(AWSCognitoAuthPlugin() as AuthPlugin<*>)
78+
Amplify.addPlugin(AWSPinpointAnalyticsPlugin())
79+
Amplify.configure(context)
80+
Sleep.milliseconds(COGNITO_CONFIGURATION_TIMEOUT)
81+
synchronousAuth = SynchronousAuth.delegatingTo(Amplify.Auth)
82+
}
83+
84+
private fun setUniqueId() {
85+
uniqueId = UUID.randomUUID().toString()
86+
preferences.edit().putString(UNIQUE_ID_KEY, uniqueId).commit()
87+
}
88+
89+
private fun readCredentialsFromResource(context: Context, @RawRes resourceId: Int): Pair<String, String>? {
90+
val resource = Resources.readAsJson(context, resourceId)
91+
var userCredentials: Pair<String, String>? = null
92+
return try {
93+
val credentials = resource.getJSONArray("credentials")
94+
for (index in 0 until credentials.length()) {
95+
val credential = credentials.getJSONObject(index)
96+
val username = credential.getString("username")
97+
val password = credential.getString("password")
98+
userCredentials = Pair(username, password)
99+
}
100+
userCredentials
101+
} catch (jsonReadingFailure: JSONException) {
102+
throw RuntimeException(jsonReadingFailure)
103+
}
104+
}
105+
106+
private fun readAppIdFromResource(context: Context, @RawRes resourceId: Int): String {
107+
val resource = Resources.readAsJson(context, resourceId)
108+
return try {
109+
val analyticsJson = resource.getJSONObject("analytics")
110+
val pluginsJson = analyticsJson.getJSONObject("plugins")
111+
val pluginJson = pluginsJson.getJSONObject("awsPinpointAnalyticsPlugin")
112+
val pinpointJson = pluginJson.getJSONObject("pinpointAnalytics")
113+
pinpointJson.getString("appId")
114+
} catch (jsonReadingFailure: JSONException) {
115+
throw RuntimeException(jsonReadingFailure)
116+
}
117+
}
118+
}
119+
120+
@Before
121+
fun flushEvents() {
122+
val context = ApplicationProvider.getApplicationContext<Context>()
123+
@RawRes val resourceId = Resources.getRawResourceId(context, CREDENTIALS_RESOURCE_NAME)
124+
val userAndPasswordPair = readCredentialsFromResource(context, resourceId)
125+
synchronousAuth.signOut()
126+
synchronousAuth.signIn(
127+
userAndPasswordPair!!.first,
128+
userAndPasswordPair.second
129+
)
130+
val hubAccumulator =
131+
HubAccumulator.create(HubChannel.ANALYTICS, AnalyticsChannelEventName.FLUSH_EVENTS, 1).start()
132+
Amplify.Analytics.flushEvents()
133+
hubAccumulator.await(10, TimeUnit.SECONDS)
134+
pinpointClient = Amplify.Analytics.getPlugin("awsPinpointAnalyticsPlugin").escapeHatch as
135+
PinpointClient
136+
uniqueId = preferences.getString(UNIQUE_ID_KEY, "error-no-unique-id")!!
137+
Assert.assertNotEquals(uniqueId, "error-no-unique-id")
138+
}
139+
140+
/**
141+
* Calls Analytics.recordEvent on an event with 5 attributes 50 times
142+
*/
143+
@Test
144+
fun testMultipleRecordEvent() {
145+
var eventName: String
146+
val hubAccumulator =
147+
HubAccumulator.create(HubChannel.ANALYTICS, AnalyticsChannelEventName.FLUSH_EVENTS, 2).start()
148+
149+
repeat(50) {
150+
eventName = "Amplify-event" + UUID.randomUUID().toString()
151+
val event = AnalyticsEvent.builder()
152+
.name(eventName)
153+
.addProperty("AnalyticsStringProperty", "Pancakes")
154+
.addProperty("AnalyticsBooleanProperty", true)
155+
.addProperty("AnalyticsDoubleProperty", 3.14)
156+
.addProperty("AnalyticsIntegerProperty", 42)
157+
.build()
158+
159+
Amplify.Analytics.recordEvent(event)
160+
}
161+
162+
Amplify.Analytics.flushEvents()
163+
val hubEvents = hubAccumulator.await(10, TimeUnit.SECONDS)
164+
val submittedEvents = combineAndFilterEvents(hubEvents)
165+
Assert.assertEquals(50, submittedEvents.size.toLong())
166+
}
167+
168+
/**
169+
* Calls Analytics.recordEvent on an event with 40 attributes 50 times
170+
*/
171+
@Test
172+
fun testLargeMultipleRecordEvent() {
173+
var eventName: String
174+
val hubAccumulator =
175+
HubAccumulator.create(HubChannel.ANALYTICS, AnalyticsChannelEventName.FLUSH_EVENTS, 2).start()
176+
177+
repeat(50) {
178+
eventName = "Amplify-event" + UUID.randomUUID().toString()
179+
val event = AnalyticsEvent.builder()
180+
event.name(eventName)
181+
for (i in 1..50) {
182+
event.addProperty("AnalyticsStringProperty$i", "Pancakes")
183+
}
184+
185+
Amplify.Analytics.recordEvent(event.build())
186+
}
187+
188+
Amplify.Analytics.flushEvents()
189+
val hubEvents = hubAccumulator.await(10, TimeUnit.SECONDS)
190+
val submittedEvents = combineAndFilterEvents(hubEvents)
191+
Assert.assertEquals(50, submittedEvents.size.toLong())
192+
}
193+
194+
/**
195+
* Calls Analytics.flushEvent 50 times
196+
*/
197+
@Test
198+
fun testMultipleFlushEvent() {
199+
val analyticsHubEventAccumulator =
200+
HubAccumulator.create(HubChannel.ANALYTICS, AnalyticsChannelEventName.FLUSH_EVENTS, 50)
201+
.start()
202+
val eventName = "Amplify-event" + UUID.randomUUID().toString()
203+
val event = AnalyticsEvent.builder()
204+
.name(eventName)
205+
.addProperty("AnalyticsStringProperty", "Pancakes")
206+
.build()
207+
Amplify.Analytics.recordEvent(event)
208+
209+
repeat(50) {
210+
Amplify.Analytics.flushEvents()
211+
Sleep.milliseconds(FLUSH_TIMEOUT)
212+
}
213+
214+
val hubEvents = analyticsHubEventAccumulator.await(10, TimeUnit.SECONDS)
215+
val submittedEvents = combineAndFilterEvents(hubEvents)
216+
Assert.assertEquals(1, submittedEvents.size.toLong())
217+
Assert.assertEquals(eventName, submittedEvents[0].name)
218+
}
219+
220+
/**
221+
* calls Analytics.recordEvent, then calls Analytics.flushEvent; 30 times
222+
*/
223+
@Test
224+
fun testFlushEvent_AfterRecordEvent() {
225+
var eventName: String
226+
val analyticsHubEventAccumulator =
227+
HubAccumulator.create(HubChannel.ANALYTICS, AnalyticsChannelEventName.FLUSH_EVENTS, 35)
228+
.start()
229+
230+
repeat(30) {
231+
eventName = "Amplify-event" + UUID.randomUUID().toString()
232+
val event = AnalyticsEvent.builder()
233+
.name(eventName)
234+
.addProperty("AnalyticsStringProperty", "Pancakes")
235+
.build()
236+
Amplify.Analytics.recordEvent(event)
237+
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
238+
Amplify.Analytics.flushEvents()
239+
Sleep.milliseconds(FLUSH_TIMEOUT)
240+
}
241+
val hubEvents = analyticsHubEventAccumulator.await(30, TimeUnit.SECONDS)
242+
val submittedEvents = combineAndFilterEvents(hubEvents)
243+
Assert.assertEquals(30, submittedEvents.size.toLong())
244+
}
245+
246+
/**
247+
* Calls Analytics.identifyUser on a user with few attributes 20 times
248+
*/
249+
@Test
250+
fun testMultipleIdentifyUser() {
251+
val location = testLocation
252+
val properties = endpointProperties
253+
val userProfile = AWSPinpointUserProfile.builder()
254+
.name("test-user")
255+
256+
.plan("test-plan")
257+
.location(location)
258+
.customProperties(properties)
259+
.build()
260+
repeat(20) {
261+
Amplify.Analytics.identifyUser(UUID.randomUUID().toString(), userProfile)
262+
Sleep.milliseconds(PINPOINT_ROUNDTRIP_TIMEOUT)
263+
val endpointResponse = fetchEndpointResponse()
264+
assertCommonEndpointResponseProperties(endpointResponse)
265+
}
266+
}
267+
268+
/**
269+
* Calls Analytics.identifyUser on a user with 100+ attributes 20 times
270+
*/
271+
@Test
272+
fun testLargeMultipleIdentifyUser() {
273+
val location = testLocation
274+
val properties = endpointProperties
275+
val userAttributes = largeUserAttributes
276+
val pinpointUserProfile = AWSPinpointUserProfile.builder()
277+
.name("test-user")
278+
279+
.plan("test-plan")
280+
.location(location)
281+
.customProperties(properties)
282+
.userAttributes(userAttributes)
283+
.build()
284+
repeat(20) {
285+
Amplify.Analytics.identifyUser(UUID.randomUUID().toString(), pinpointUserProfile)
286+
Sleep.milliseconds(PINPOINT_ROUNDTRIP_TIMEOUT)
287+
val endpointResponse = fetchEndpointResponse()
288+
assertCommonEndpointResponseProperties(endpointResponse)
289+
}
290+
}
291+
292+
private fun fetchEndpointResponse(): EndpointResponse {
293+
var endpointResponse: EndpointResponse? = null
294+
runBlocking {
295+
endpointResponse = pinpointClient.getEndpoint(
296+
GetEndpointRequest.invoke {
297+
this.applicationId = appId
298+
this.endpointId = uniqueId
299+
}
300+
).endpointResponse
301+
}
302+
assert(null != endpointResponse)
303+
return endpointResponse!!
304+
}
305+
306+
private fun assertCommonEndpointResponseProperties(endpointResponse: EndpointResponse) {
307+
Log.i("DEBUG", endpointResponse.toString())
308+
val attributes = endpointResponse.attributes!!
309+
Assert.assertEquals("[email protected]", attributes["email"]!![0])
310+
Assert.assertEquals("test-user", attributes["name"]!![0])
311+
Assert.assertEquals("test-plan", attributes["plan"]!![0])
312+
val endpointProfileLocation: EndpointLocation = endpointResponse.location!!
313+
Assert.assertEquals(47.6154086, endpointProfileLocation.latitude, 0.1)
314+
Assert.assertEquals((-122.3349685), endpointProfileLocation.longitude, 0.1)
315+
Assert.assertEquals("98122", endpointProfileLocation.postalCode)
316+
Assert.assertEquals("Seattle", endpointProfileLocation.city)
317+
Assert.assertEquals("WA", endpointProfileLocation.region)
318+
Assert.assertEquals("USA", endpointProfileLocation.country)
319+
Assert.assertEquals("TestStringValue", attributes["TestStringProperty"]!![0])
320+
Assert.assertEquals(1.0, endpointResponse.metrics!!["TestDoubleProperty"]!!, 0.1)
321+
}
322+
323+
private val largeUserAttributes: AnalyticsProperties
324+
get() {
325+
val analyticsProperties = AnalyticsProperties.builder()
326+
for (i in 1..100) {
327+
analyticsProperties.add("SomeUserAttribute$i", "User attribute value")
328+
}
329+
return analyticsProperties.build()
330+
}
331+
332+
private val endpointProperties: AnalyticsProperties
333+
get() {
334+
return AnalyticsProperties.builder()
335+
.add("TestStringProperty", "TestStringValue")
336+
.add("TestDoubleProperty", 1.0)
337+
.build()
338+
}
339+
private val testLocation: UserProfile.Location
340+
get() {
341+
return UserProfile.Location.builder()
342+
.latitude(47.6154086)
343+
.longitude(-122.3349685)
344+
.postalCode("98122")
345+
.city("Seattle")
346+
.region("WA")
347+
.country("USA")
348+
.build()
349+
}
350+
351+
private fun combineAndFilterEvents(hubEvents: List<HubEvent<*>>): MutableList<AnalyticsEvent> {
352+
val result = mutableListOf<AnalyticsEvent>()
353+
hubEvents.forEach {
354+
if ((it.data as List<*>).isNotEmpty()) {
355+
(it.data as ArrayList<*>).forEach { event ->
356+
if (!(event as AnalyticsEvent).name.startsWith("_session")) {
357+
result.add(event)
358+
}
359+
}
360+
}
361+
}
362+
return result
363+
}
364+
}

aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public void adapterCanDeserializeJsonOfSerializedModelIntoMwm() throws AmplifyEx
140140
String json = Resources.readAsString("serialized-model-with-metadata.json");
141141
Type type = TypeMaker.getParameterizedType(ModelWithMetadata.class, SerializedModel.class);
142142
ModelWithMetadata<SerializedModel> actual = gson.fromJson(json, type);
143-
143+
144144
// Assert that the deserialized output matches out expected value
145145
Assert.assertEquals(expected, actual);
146146
}

0 commit comments

Comments
 (0)