Skip to content

Commit 0da043d

Browse files
author
talkol
committed
improved stress tests to check RN flakiness
1 parent 19d519a commit 0da043d

File tree

9 files changed

+155
-72
lines changed

9 files changed

+155
-72
lines changed

detox/test/e2e/c-stress-tests.js

+25-14
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,37 @@ describe.skip('Stress Tests', function () {
66

77
beforeEach(function () {
88
element(by.label('Stress')).tap();
9-
})
9+
});
10+
11+
it('should handle tap during busy bridge (one way)', function () {
12+
element(by.label('Bridge OneWay Stress')).tap();
13+
element(by.label('Next')).tap();
14+
expect(element(by.label('BridgeOneWay'))).toBeVisible();
15+
});
16+
17+
it('should handle tap during busy bridge (two way)', function () {
18+
element(by.label('Bridge TwoWay Stress')).tap();
19+
element(by.label('Next')).tap();
20+
expect(element(by.label('BridgeTwoWay'))).toBeVisible();
21+
});
1022

11-
it('should handle tap during busy bridge', function () {
12-
element(by.label('Bridge Stress')).tap();
13-
expect(element(by.label('Hello World!!!'))).toBeVisible();
23+
it('should handle tap during busy bridge (setState)', function () {
24+
element(by.label('Bridge setState Stress')).tap();
25+
element(by.label('Next')).tap();
26+
expect(element(by.label('BridgeSetState'))).toBeVisible();
1427
});
1528

1629
it('should handle tap during busy JS event loop', function () {
17-
element(by.label('Events Stress')).tap();
18-
expect(element(by.label('Hello World!!!'))).toBeVisible();
30+
element(by.label('EventLoop Stress')).tap();
31+
element(by.label('Next')).tap();
32+
expect(element(by.label('EventLoop'))).toBeVisible();
1933
});
2034

21-
describe('Consecutive Test', function () {
22-
const MULTI_TEST_COUNT = 20;
23-
for (let i = 0; i < MULTI_TEST_COUNT; i++) {
24-
it(`should handle tap in consecutive test #${i+1}`, function () {
25-
element(by.label('Say Hello')).tap();
26-
expect(element(by.label('Hello!!!'))).toBeVisible();
27-
});
28-
};
35+
it('should handle consecutive taps', function () {
36+
const TAP_COUNT = 50;
37+
for (let i = 1 ; i <= TAP_COUNT ; i++) {
38+
element(by.label('Consecutive Stress ' + i)).tap();
39+
}
2940
});
3041

3142
});

detox/test/index.ios.js

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import * as Screens from './src/screens'
99

1010
class example extends Component {
11+
1112
constructor(props) {
1213
super(props);
1314
this.state = {
@@ -42,6 +43,7 @@ class example extends Component {
4243
<Screen />
4344
);
4445
}
46+
4547
}
4648

4749
AppRegistry.registerComponent('example', () => example);

detox/test/ios/example.xcodeproj/project.pbxproj

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
2424
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
2525
CC0F35311D461096008BB94F /* Detox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CCFA7DF51D11D25A00E15EDF /* Detox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
26+
CC17D3321D60A24300267B0C /* NativeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = CC17D3311D60A24300267B0C /* NativeModule.m */; };
2627
/* End PBXBuildFile section */
2728

2829
/* Begin PBXContainerItemProxy section */
@@ -162,6 +163,8 @@
162163
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
163164
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
164165
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
166+
CC17D3301D60A24300267B0C /* NativeModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeModule.h; path = example/NativeModule.h; sourceTree = "<group>"; };
167+
CC17D3311D60A24300267B0C /* NativeModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NativeModule.m; path = example/NativeModule.m; sourceTree = "<group>"; };
165168
CCFA7DEF1D11D25A00E15EDF /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = ../node_modules/detox/ios/Detox.xcodeproj; sourceTree = "<group>"; };
166169
/* End PBXFileReference section */
167170

@@ -276,6 +279,8 @@
276279
13B07FB61A68108700A75B9A /* Info.plist */,
277280
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
278281
13B07FB71A68108700A75B9A /* main.m */,
282+
CC17D3301D60A24300267B0C /* NativeModule.h */,
283+
CC17D3311D60A24300267B0C /* NativeModule.m */,
279284
);
280285
name = example;
281286
sourceTree = "<group>";
@@ -610,6 +615,7 @@
610615
isa = PBXSourcesBuildPhase;
611616
buildActionMask = 2147483647;
612617
files = (
618+
CC17D3321D60A24300267B0C /* NativeModule.m in Sources */,
613619
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
614620
13B07FC11A68108700A75B9A /* main.m in Sources */,
615621
);

detox/test/ios/example/AppDelegate.h

-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
/**
2-
* Copyright (c) 2015-present, Facebook, Inc.
3-
* All rights reserved.
4-
*
5-
* This source code is licensed under the BSD-style license found in the
6-
* LICENSE file in the root directory of this source tree. An additional grant
7-
* of patent rights can be found in the PATENTS file in the same directory.
8-
*/
9-
101
#import <UIKit/UIKit.h>
112

123
@interface AppDelegate : UIResponder <UIApplicationDelegate>

detox/test/ios/example/AppDelegate.m

-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
/**
2-
* Copyright (c) 2015-present, Facebook, Inc.
3-
* All rights reserved.
4-
*
5-
* This source code is licensed under the BSD-style license found in the
6-
* LICENSE file in the root directory of this source tree. An additional grant
7-
* of patent rights can be found in the PATENTS file in the same directory.
8-
*/
9-
101
#import "AppDelegate.h"
112
#import "RCTRootView.h"
123
#import "DetoxLoader.h"

detox/test/ios/example/NativeModule.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#import "RCTBridgeModule.h"
2+
#import <Foundation/Foundation.h>
3+
4+
@interface NativeModule : NSObject <RCTBridgeModule>
5+
6+
@end

detox/test/ios/example/NativeModule.m

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#import "NativeModule.h"
2+
3+
@implementation NativeModule
4+
5+
RCT_EXPORT_MODULE();
6+
7+
RCT_EXPORT_METHOD(echoWithoutResponse:(NSString *)str)
8+
{
9+
NSLog(@"NativeModule echoWithoutResponse called");
10+
}
11+
12+
RCT_EXPORT_METHOD(echoWithResponse:(NSString *)str
13+
resolver:(RCTPromiseResolveBlock)resolve
14+
rejecter:(RCTPromiseRejectBlock)reject)
15+
{
16+
resolve(str);
17+
NSLog(@"NativeModule echoWithResponse called");
18+
}
19+
20+
@end

detox/test/src/Screens/SanityScreen.js

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from 'react-native';
77

88
export default class SanityScreen extends Component {
9+
910
constructor(props) {
1011
super(props);
1112
this.state = {
@@ -34,6 +35,7 @@ export default class SanityScreen extends Component {
3435
</View>
3536
);
3637
}
38+
3739
renderAfterButton() {
3840
return (
3941
<View style={{flex: 1, paddingTop: 20, justifyContent: 'center', alignItems: 'center'}}>

detox/test/src/Screens/StressScreen.js

+94-40
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,37 @@ import React, { Component } from 'react';
22
import {
33
Text,
44
View,
5-
TouchableOpacity
5+
TouchableOpacity,
6+
NativeModules
67
} from 'react-native';
78

8-
const STRESSFUL_STRING_LENGTH = 48000; // Min: 32000
9-
const STRESSFUL_EVENTS_COUNT = 570; // Min: 380
9+
const NativeModule = NativeModules.NativeModule;
1010

11-
function buildStringByLength(length) {
12-
str = "";
13-
charcode = 65;
14-
for (let i=0; i < length; i++) {
15-
str += String.fromCharCode(charcode);
16-
charcode ++;
17-
if (charcode == 91) charcode = 65;
11+
const BRIDGE_ONEWAY_CALLS = 10;
12+
const BRIDGE_ONEWAY_STR_CHUNK_LEN = 20;
13+
const BRIDGE_TWOWAY_CALLS = 10;
14+
const BRIDGE_TWOWAY_STR_CHUNK_LEN = 20;
15+
const BRIDGE_SETSTATE_STR_CHUNK_LEN = 20;
16+
const EVENT_LOOP_COUNT = 10;
17+
const EVENT_LOOP_STR_CHUNK_LEN = 20;
18+
19+
function getStringByLength(chunks) {
20+
let res = '';
21+
for (let i = 0; i < chunks ; i++) {
22+
res += 'EqtCfLH6DYnLT4WjBcLfR9M33uxSEEBMphVSTnpKpEfHCBNn3oxVMpEQ0Rzqlx8BiiyCIF5WnkEhJyGsGhHtVfjgwCueY0DQXmat';
1823
}
19-
return str;
24+
return res;
2025
}
2126

2227
export default class StressScreen extends Component {
28+
2329
constructor(props) {
2430
super(props);
2531
this.state = {
26-
greeting: undefined,
27-
passToBridge: undefined
32+
phase1: undefined,
33+
phase2: undefined,
34+
extraData: undefined,
35+
counter: 1
2836
};
2937
}
3038

@@ -37,56 +45,102 @@ export default class StressScreen extends Component {
3745
}
3846

3947
render() {
40-
if (this.state.greeting) return this.renderAfterButton();
48+
if (this.state.phase2) return this.renderPhase2();
49+
if (this.state.phase1) return this.renderPhase1();
4150
return (
4251
<View style={{flex: 1, paddingTop: 20, justifyContent: 'center', alignItems: 'center'}}>
43-
<Text style={{fontSize: 25, marginBottom: 30}}>
44-
Welcome
45-
</Text>
46-
{this.renderTestButton('Say Hello', this.onButtonPress.bind(this, 'Hello'))}
47-
{this.renderTestButton('Say World', this.onButtonPress.bind(this, 'World'))}
48-
{this.renderTestButton('Bridge Stress', this.bridgeStressButtonPressed.bind(this, 'Hello World'))}
49-
{this.renderTestButton('Events Stress', this.eventsStressButtonPressed.bind(this, 'Hello World'))}
52+
{this.renderTestButton('Bridge OneWay Stress', this.bridgeOneWayStressButtonPressed.bind(this))}
53+
{this.renderTestButton('Bridge TwoWay Stress', this.bridgeTwoWayStressButtonPressed.bind(this))}
54+
{this.renderTestButton('Bridge setState Stress', this.bridgeSetStateStressButtonPressed.bind(this))}
55+
{this.renderTestButton('EventLoop Stress', this.eventLoopStressButtonPressed.bind(this))}
56+
{this.renderTestButton(`Consecutive Stress ${this.state.counter}`, this.consecutiveStressButtonPressed.bind(this))}
5057
</View>
5158
);
5259
}
53-
renderAfterButton() {
60+
61+
renderPhase2() {
5462
return (
5563
<View style={{flex: 1, paddingTop: 20, justifyContent: 'center', alignItems: 'center'}}>
56-
<Text style={{fontSize: 10, width: 0, height: 0}}>
57-
Bridge: {this.state.passToBridge}
58-
</Text>
59-
<Text style={{fontSize: 25}}>
60-
{this.state.greeting}!!!
64+
<Text style={{fontSize: 25, marginBottom: 20}}>
65+
{this.state.phase2}
6166
</Text>
67+
{
68+
!this.state.extraData ? false :
69+
<Text style={{fontSize: 10, width: 100, height: 20}}>
70+
Extra Data: {this.state.extraData}
71+
</Text>
72+
}
6273
</View>
6374
);
6475
}
6576

66-
onButtonPress(greeting) {
77+
renderPhase1() {
78+
return (
79+
<View style={{flex: 1, paddingTop: 20, justifyContent: 'center', alignItems: 'center'}}>
80+
<TouchableOpacity onPress={this.onButtonPress.bind(this)}>
81+
<Text style={{color: 'blue', marginBottom: 20}}>Next</Text>
82+
</TouchableOpacity>
83+
</View>
84+
);
85+
}
86+
87+
onButtonPress() {
88+
this.setState({
89+
phase2: this.state.phase1
90+
});
91+
}
92+
93+
bridgeOneWayStressButtonPressed() {
6794
this.setState({
68-
greeting: greeting
95+
phase1: 'BridgeOneWay'
96+
});
97+
setImmediate(() => {
98+
const str = getStringByLength(BRIDGE_ONEWAY_STR_CHUNK_LEN);
99+
for (let i = 0 ; i < BRIDGE_ONEWAY_CALLS ; i++) {
100+
NativeModule.echoWithoutResponse(str);
101+
}
69102
});
70103
}
71104

72-
bridgeStressButtonPressed(greeting) {
73-
this.onButtonPress(greeting)
105+
bridgeTwoWayStressButtonPressed() {
106+
this.setState({
107+
phase1: 'BridgeTwoWay'
108+
});
109+
setImmediate(() => {
110+
const str = getStringByLength(BRIDGE_TWOWAY_STR_CHUNK_LEN);
111+
for (let i = 0 ; i < BRIDGE_TWOWAY_CALLS ; i++) {
112+
NativeModule.echoWithResponse(str);
113+
}
114+
});
115+
}
74116

75-
const data = buildStringByLength(STRESSFUL_STRING_LENGTH);
117+
bridgeSetStateStressButtonPressed() {
76118
this.setState({
77-
passToBridge: data,
78-
greeting: greeting
119+
phase1: 'BridgeSetState'
120+
});
121+
setImmediate(() => {
122+
const str = getStringByLength(BRIDGE_SETSTATE_STR_CHUNK_LEN);
123+
this.setState({
124+
extraData: str
125+
});
79126
});
80127
}
81128

82-
eventsStressButtonPressed(greeting) {
83-
// Stress:
84-
for (let i =0; i < STRESSFUL_EVENTS_COUNT; i++) {
85-
let myString = "";
86-
setImmediate(() => { myString = buildStringByLength(1000) });
129+
eventLoopStressButtonPressed() {
130+
this.setState({
131+
phase1: 'EventLoop'
132+
});
133+
for (let i = 0 ; i < EVENT_LOOP_COUNT ; i++) {
134+
setImmediate(() => {
135+
let str = getStringByLength(EVENT_LOOP_STR_CHUNK_LEN);
136+
});
87137
}
138+
}
88139

89-
setImmediate(() => { this.onButtonPress(greeting) });
140+
consecutiveStressButtonPressed() {
141+
this.setState({
142+
counter: this.state.counter + 1
143+
});
90144
}
91145

92146
}

0 commit comments

Comments
 (0)