Skip to content

Commit

Permalink
Add performance tests (#384)
Browse files Browse the repository at this point in the history
  • Loading branch information
Amir-P authored Aug 18, 2024
1 parent 82e57da commit 7f965d0
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 29 deletions.
67 changes: 67 additions & 0 deletions packages/fleather/example/integration_test/editing_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'package:fleather/fleather.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_test_robots/flutter_test_robots.dart';
import 'package:integration_test/integration_test.dart';
import 'package:parchment/codecs.dart';

void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets('Enter some text at the end', (tester) async {
final document = const ParchmentMarkdownCodec().decode(markdown * 100);
final controller = FleatherController(document: document);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: FleatherEditor(controller: controller),
),
));

await binding.traceAction(
() async {
await tester.tap(find.byType(RawEditor));
await tester.pump();
controller.updateSelection(
TextSelection.collapsed(offset: document.length - 1));
await tester.pump();
await tester.ime.typeText(iputText, finder: find.byType(RawEditor));
},
reportKey: 'timeline',
);
});
}

const iputText =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';

const markdown = '''
# Fleather
_Soft and gentle rich text editing for Flutter applications._
Fleather is an **early preview** open source library.
- [ ] That even supports
- [X] Checklists
### Documentation
* Quick Start
* Data format and Document Model
* Style attributes
* Heuristic rules
## Clean and modern look
Fleather’s rich text editor is built with _simplicity and flexibility_ in mind. It provides clean interface for distraction-free editing. Think `Medium.com`-like experience.
```
import ‘package:flutter/material.dart’;
import ‘package:parchment/parchment.dart’;
void main() {
print(“Hello world!”);
}
```
''';
69 changes: 69 additions & 0 deletions packages/fleather/example/integration_test/scrolling_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import 'package:fleather/fleather.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:parchment/codecs.dart';

void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets('Scroll to the end', (tester) async {
final document = const ParchmentMarkdownCodec().decode(markdown * 100);
final controller = FleatherController(document: document);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: FleatherEditor(controller: controller),
),
));

final scrollableFinder = find.byType(Scrollable);
final scrollable = tester.widget<Scrollable>(scrollableFinder);
final scrollController = scrollable.controller!;

await binding.traceAction(
() async {
while (scrollController.position.extentAfter != 0) {
await tester.drag(scrollableFinder, const Offset(0, -500));
await tester.pump();
}
while (scrollController.position.extentBefore != 0) {
await tester.drag(scrollableFinder, const Offset(0, 500));
await tester.pump();
}
},
reportKey: 'timeline',
);
});
}

const markdown = '''
# Fleather
_Soft and gentle rich text editing for Flutter applications._
Fleather is an **early preview** open source library.
- [ ] That even supports
- [X] Checklists
### Documentation
* Quick Start
* Data format and Document Model
* Style attributes
* Heuristic rules
## Clean and modern look
Fleather’s rich text editor is built with _simplicity and flexibility_ in mind. It provides clean interface for distraction-free editing. Think `Medium.com`-like experience.
```
import ‘package:flutter/material.dart’;
import ‘package:parchment/parchment.dart’;
void main() {
print(“Hello world!”);
}
```
''';
8 changes: 8 additions & 0 deletions packages/fleather/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependencies:
image_picker: ^1.0.7
fleather:
path: ../
parchment:
path: ../../parchment

dependency_overrides:
parchment:
Expand All @@ -25,6 +27,12 @@ dependency_overrides:
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
flutter_driver:
sdk: flutter
flutter_lints: ^4.0.0
flutter_test_robots: ^0.0.24

flutter:
uses-material-design: true
Expand Down
29 changes: 0 additions & 29 deletions packages/fleather/example/test/widget_test.dart

This file was deleted.

19 changes: 19 additions & 0 deletions packages/fleather/example/test_driver/performance_driver.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'dart:io';

import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() {
final outputName = Platform.environment['FLEATHER_PERF_TEST_OUTPUT_NAME']!;
return integrationDriver(
responseDataCallback: (data) async {
if (data != null) {
final timeline =
driver.Timeline.fromJson(data['timeline'] as Map<String, dynamic>);
final summary = driver.TimelineSummary.summarize(timeline);
await summary.writeTimelineToFile(outputName,
pretty: true, includeSummary: true);
}
},
);
}
93 changes: 93 additions & 0 deletions packages/fleather/example/test_utils/analyze_performance.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// ignore_for_file: avoid_print

import 'dart:convert';
import 'dart:io';

class Criteria {
final String key, title;
final double threshold;

const Criteria(this.key, this.title, {this.threshold = 0.1});

bool isRegression(double target, double reference) {
final double changeRatio = (target - reference) / reference;
return changeRatio > threshold;
}

bool isWorse(double target, double reference) => target > reference;
}

const _criterias = [
Criteria('average_frame_build_time_millis', 'Average Frame Build Time'),
Criteria('90th_percentile_frame_build_time_millis',
'90th Percentile Frame Build Time'),
Criteria('99th_percentile_frame_build_time_millis',
'90th Percentile Frame Build Time'),
Criteria('average_gpu_frame_time', 'Average GPU Frame Time'),
Criteria('average_memory_usage', 'Average Memory Usage'),
Criteria('average_cpu_usage', 'Average CPU Usage'),
];

void main(List<String> args) {
final outputBuffer = StringBuffer();
bool hasRegression = false;

final reference = args[0];
final target = args[1];

final result = _analyze(reference, target);
hasRegression = hasRegression || result.$2;
outputBuffer.writeln(result.$1);

print(outputBuffer.toString());
exit(hasRegression ? 1 : 0);
}

(String, bool) _analyze(String reference, String target) {
final Map<String, dynamic> referenceSummary =
jsonDecode(File(reference).readAsStringSync());
final Map<String, dynamic> targetSummary =
jsonDecode(File(target).readAsStringSync());

bool testHasRegression = false;
final outputBuffer = StringBuffer();

for (final criteria in _criterias) {
if (!targetSummary.containsKey(criteria.key)) continue;
bool criteriaHasRegression = false;
final double targetValue = targetSummary[criteria.key];
final double referenceValue = referenceSummary[criteria.key];
criteriaHasRegression = criteria.isRegression(targetValue, referenceValue);
outputBuffer.write('${criteria.title}: Target: ');
if (criteria.isWorse(targetValue, referenceValue)) {
outputBuffer.write(buildErrorMessage(targetValue.toStringAsFixed(2)));
} else {
outputBuffer.write(targetValue.toStringAsFixed(2));
}
outputBuffer.writeln(' Reference: ${referenceValue.toStringAsFixed(2)}');
testHasRegression = testHasRegression || criteriaHasRegression;
}

outputBuffer.write('Performance tests found ');
if (testHasRegression) {
outputBuffer.writeln(buildErrorMessage('regression'));
} else {
outputBuffer.writeln(buildSuccessMessage('no regression'));
}

return (outputBuffer.toString(), testHasRegression);
}

void assertTrueOrExit(bool assertion, [String? message]) {
if (!assertion) {
if (message != null) {
error(message);
}
exit(1);
}
}

void error(String message) => print(buildErrorMessage(message));

String buildErrorMessage(String message) => '\x1B[31m$message\x1B[0m';
String buildSuccessMessage(String message) => '\x1B[32m$message\x1B[0m';
Loading

0 comments on commit 7f965d0

Please sign in to comment.