Skip to content

Commit

Permalink
test: add memory leak test and ci.
Browse files Browse the repository at this point in the history
  • Loading branch information
andycall committed Sep 25, 2023
1 parent e9a9990 commit d952fe9
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 26 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/integration_test_flutter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,30 @@ jobs:
- name: Check on failures
if: steps.test.outcome != 'success'
run: exit 1

memory_leak_test:
runs-on: self-hosted
needs: [ build_bridge ]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-node@v2
with:
node-version: ${{ env.nodeVersion }}
- uses: actions/download-artifact@v2
with:
name: macos_bridge_binary
path: bridge/build/macos/
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.flutter310 }}
- run: flutter config --enable-macos-desktop
- run: flutter doctor -v
- name: Run MemoryLeak Test
run: cd integration_tests && npm run memory_leak_integration
id: test
continue-on-error: true
- name: Check on failures
if: steps.test.outcome != 'success'
run: exit 1
43 changes: 19 additions & 24 deletions integration_tests/lib/memory_leak.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,17 @@ ContentType getFileContentType(String fileName) {
return javascriptContentType;
}


Future<void> run(AsyncCallback callback) async {
await callback();
Future<void> sleep(Duration duration) {
Completer completer = Completer();
Timer(duration, () async {
completer.complete();
});
return completer.future;
}

Future<void> runWithMultiple(AsyncCallback callback, int multipleTime) async {
for (int i = 0; i < multipleTime; i ++) {
await run(callback);
await callback();
}
}

Expand All @@ -194,34 +197,26 @@ class HomePageElement extends StatelessElement {

await runWithMultiple(() async {
await runWithMultiple(() async {
await run(() async {
Completer completer = Completer();
Timer(Duration(seconds: 1), () async {
Navigator.pushNamed(this, '/' + current.name);
completer.complete();
});
return completer.future;
});
await run(() async {
Completer completer = Completer();
Timer(Duration(seconds: 1), () async {
Navigator.pop(this);
completer.complete();
});
return completer.future;
});
}, 1);
await sleep(Duration(seconds: 1));
Navigator.pushNamed(this, '/' + current.name);
await sleep(Duration(seconds: 1));
Navigator.pop(this);
}, 5);

if (currentIndex < codes.length - 1) {
current = codes[currentIndex + 1];
currentIndex = currentIndex + 1;
}
}, codes.length);

print(mems);
print(isMemLeaks(mems));
await sleep(Duration(seconds: 1));
bool isLeaked = isMemLeaks(mems);

print('done');
print('memory leaks: ${isMemLeaks(mems)}');
if (isLeaked) {
exit(1);
}
exit(0);
}
}

Expand Down
17 changes: 16 additions & 1 deletion integration_tests/lib/utils/mem_leak_detector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,27 @@ double linearRegressionSlope(List<double> x, List<double> y) {
return (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
}

bool memoryIncreaseRatio(List<double> memoryUsage) {
if (memoryUsage.length < 2) return false;

int increasingCount = 0;
for (int i = 1; i < memoryUsage.length; i++) {
if (memoryUsage[i] > memoryUsage[i - 1]) {
increasingCount++;
}
}

double ratio = increasingCount / memoryUsage.length;
return ratio > 0.75;
}


bool detectMemoryLeakBasedOnRegression(List<double> memoryUsage) {
List<double> timePoints = List.generate(memoryUsage.length, (index) => index.toDouble()); // [0.0, 1.0, 2.0, ...]
double slope = linearRegressionSlope(timePoints, memoryUsage);
return slope > 5; // 如果斜率大于0,则判断为可能存在内存泄漏
}

bool isMemLeaks(List<double> mems) {
return detectMemoryLeakBasedOnRegression(mems);
return detectMemoryLeakBasedOnRegression(mems) && memoryIncreaseRatio(mems);
}
3 changes: 2 additions & 1 deletion integration_tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"specs": "webpack --config ./webpack.config.js",
"prettier": "prettier --no-config --single-quote --trailing-comma=es5 --write ./specs/{.,**,**/**}/*.ts",
"integration": "npm run clean && npm run specs && node scripts/core_integration_starter",
"multiple_page_integration": "node scripts/multiple_page_integration"
"multiple_page_integration": "node scripts/multiple_page_integration",
"memory_leak_integration": "node scripts/memory_leak_integration"
},
"devDependencies": {
"@babel/core": "^7.12.10",
Expand Down
57 changes: 57 additions & 0 deletions integration_tests/scripts/memory_leak_integration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2022-present The Kraken authors. All rights reserved.
*/
const { spawn, spawnSync } = require('child_process');
const path = require('path');
const os = require('os');

// Dart null safety error didn't report in dist binaries. Should run integration test with flutter run directly.
function startIntegrationTest() {
const shouldSkipBuild = /skip\-build/.test(process.argv);
if (!shouldSkipBuild) {
spawnSync('flutter', ['build', 'macos', '--profile', '--target=lib/memory_leak.dart'], {
stdio: 'inherit'
});
}

const platform = os.platform();
let testExecutable;
if (platform === 'linux') {
testExecutable = path.join(__dirname, '../build/linux/x64/profile/bundle/app');
} else if (platform === 'darwin') {
testExecutable = path.join(__dirname, '../build/macos/Build/Products/Profile/tests.app/Contents/MacOS/tests');
} else {
throw new Error('Unsupported platform:' + platform);
}

const tester = spawn(testExecutable, [], {
env: {
...process.env,
WEBF_ENABLE_TEST: 'true',
'enable-software-rendering': true,
'skia-deterministic-rendering': true,
WEBF_TEST_DIR: path.join(__dirname, '../')
},
cwd: process.cwd(),
stdio: 'inherit'
});

tester.on('close', (code) => {
process.exit(code);
});
tester.on('error', (error) => {
console.error('integration failed', error);
process.exit(1);
});
tester.on('exit', (code, signal) => {
if (signal) {
console.log('Process exit with ' + signal);
process.exit(1);
}
if (code != 0) {
process.exit(1);
}
});
}

startIntegrationTest();

0 comments on commit d952fe9

Please sign in to comment.