Skip to content

Commit

Permalink
Add benchmarks for Artefact Page (#192)
Browse files Browse the repository at this point in the history
* Add benchmarks for Artefact Page

* add missing trailing commas
  • Loading branch information
omar-selo authored Jul 22, 2024
1 parent 1450a0c commit e8b9fef
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 1 deletion.
5 changes: 4 additions & 1 deletion frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ doc/api/
*.freezed.dart
*.g.dart

custom_lint.log
custom_lint.log

# Benchmark results
benchmarks/*.json
14 changes: 14 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ And you can run all integration tests via the bash script:
$ ./run_integration_tests.sh
```

## Benchmark

There are some benchmarks written under the `/benchmarks` directory. You can run these using:

```bash
$ dart run_benchmark_tests.dart
```

This will open up chrome and run the benchmarks infront of you.

You can also pass `--headless` to run the above command in headless mode.

Either way the command will produce json files with results under the `/benchmarks` directory.

## Connect to backend

By default the Test Observer frontend will use `http://localhost:30000/` to communicate with the backend. To use a different address you can modify `window.testObserverAPIBaseURI` attribute in `web/index.html`.
36 changes: 36 additions & 0 deletions frontend/benchmarks/artefact_page_scrolling.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:web_benchmarks/client.dart';

import 'common.dart';

class ScrollRecorder extends AppRecorder {
ScrollRecorder() : super(benchmarkName: 'scrolling');

@override
Future<void> start() async {
final firstTestExecution = find.text('Environment 0').evaluate().first;
final scrollable = Scrollable.of(firstTestExecution);

while (true) {
await scrollable.position.animateTo(
scrollable.position.maxScrollExtent,
curve: Curves.linear,
duration: const Duration(seconds: 10),
);
await scrollable.position.animateTo(
scrollable.position.minScrollExtent,
curve: Curves.linear,
duration: const Duration(seconds: 10),
);
}
}
}

Future<void> main() async {
await runBenchmarks(
<String, RecorderFactory>{
'scrolling': () => ScrollRecorder(),
},
);
}
39 changes: 39 additions & 0 deletions frontend/benchmarks/artefact_page_tapping.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:web_benchmarks/client.dart';

import 'common.dart';

class TapRecorder extends AppRecorder {
TapRecorder() : super(benchmarkName: 'tapping');

@override
Future<void> start() async {
final controller = LiveWidgetController(WidgetsBinding.instance);
final firstTestExecution = find.text('Environment 0');
final failedSection = find.textContaining(RegExp(r'Failed \d+'));
final passedSection = find.textContaining(RegExp(r'Passed \d+'));
final skippedSection = find.textContaining(RegExp(r'Skipped \d+'));

while (true) {
await controller.tap(firstTestExecution);
await controller.pumpAndSettle();
await controller.tap(failedSection);
await controller.pumpAndSettle();
await controller.tap(skippedSection);
await controller.pumpAndSettle();
await controller.tap(passedSection);
await controller.pumpAndSettle();
await controller.tap(firstTestExecution);
await controller.pumpAndSettle();
}
}
}

Future<void> main() async {
await runBenchmarks(
<String, RecorderFactory>{
'tapping': () => TapRecorder(),
},
);
}
117 changes: 117 additions & 0 deletions frontend/benchmarks/common.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mocktail/mocktail.dart';
import 'package:testcase_dashboard/app.dart';
import 'package:testcase_dashboard/models/artefact.dart';
import 'package:testcase_dashboard/models/artefact_build.dart';
import 'package:testcase_dashboard/models/environment.dart';
import 'package:testcase_dashboard/models/family_name.dart';
import 'package:testcase_dashboard/models/stage_name.dart';
import 'package:testcase_dashboard/models/test_execution.dart';
import 'package:testcase_dashboard/models/test_result.dart';
import 'package:testcase_dashboard/providers/api.dart';
import 'package:testcase_dashboard/repositories/api_repository.dart';
import 'package:web_benchmarks/client.dart';

abstract class AppRecorder extends WidgetRecorder {
AppRecorder({required this.benchmarkName}) : super(name: benchmarkName);

final String benchmarkName;

Future<void> start();

Future<void> animationStops() async {
while (WidgetsBinding.instance.hasScheduledFrame) {
await Future.delayed(const Duration(milliseconds: 200));
}
}

@override
Widget createWidget() {
Future.delayed(const Duration(milliseconds: 400), start);
return ProviderScope(
// ignore: scoped_providers_should_specify_dependencies
overrides: [apiProvider.overrideWithValue(ApiRepositoryMock())],
child: const App(),
);
}
}

class ApiRepositoryMock extends Mock implements ApiRepository {
@override
Future<Map<int, Artefact>> getFamilyArtefacts(FamilyName family) async {
const dummyArtefact = Artefact(
id: 1,
name: 'artefact',
version: '1',
track: 'latest',
store: 'ubuntu',
series: '',
repo: '',
status: ArtefactStatus.undecided,
stage: StageName.beta,
bugLink: '',
);

return {
for (int i = 0; i < 100; i++)
i: dummyArtefact.copyWith(id: i, name: 'artefact $i'),
};
}

@override
Future<List<ArtefactBuild>> getArtefactBuilds(int artefactId) async {
return [
ArtefactBuild(
id: 1,
architecture: 'amd64',
revision: 1,
testExecutions: [
for (int i = 0; i < 200; i++)
TestExecution(
id: i,
ciLink: 'ciLink',
c3Link: 'c3Link',
status: TestExecutionStatus.failed,
environment: Environment(
id: i,
name: 'Environment $i',
architecture: 'amd64',
),
reviewComment: 'reviewComment',
reviewDecision: [],
),
],
),
ArtefactBuild(
id: 2,
architecture: 'armhf',
revision: 1,
testExecutions: [
for (int i = 200; i < 400; i++)
TestExecution(
id: i,
ciLink: 'ciLink',
c3Link: 'c3Link',
status: TestExecutionStatus.failed,
environment: Environment(
id: i,
name: 'Environment $i',
architecture: 'armhf',
),
reviewComment: 'reviewComment',
reviewDecision: [],
),
],
),
];
}

@override
Future<List<TestResult>> getTestExecutionResults(int testExecutionId) async {
return [
for (int i = 0; i < 300; i++)
TestResult(name: 'result $i', status: TestResultStatus.passed),
];
}
}
8 changes: 8 additions & 0 deletions frontend/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.1"
web_benchmarks:
dependency: "direct dev"
description:
name: web_benchmarks
sha256: e791639c8e96f5bdeef9a3e6c79676187246deca71becc18b7b6ad57ba8d3210
url: "https://pub.dev"
source: hosted
version: "2.0.0"
web_socket_channel:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions frontend/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
web_benchmarks: ^2.0.0
flutter_lints: ^2.0.0
dependency_validator: ^3.2.2
build_runner: ^2.3.3
Expand Down
44 changes: 44 additions & 0 deletions frontend/run_benchmark_tests.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'dart:convert' show JsonEncoder;
import 'dart:io';

import 'package:web_benchmarks/server.dart';

Future<void> main(List<String> args) async {
final isHeadless = args.contains('--headless');

await runBenchmarkRunnerFile(
'benchmarks/artefact_page_tapping.dart',
'#/snaps/0',
isHeadless,
);
await runBenchmarkRunnerFile(
'benchmarks/artefact_page_scrolling.dart',
'#/snaps/0',
isHeadless,
);
}

Future<void> runBenchmarkRunnerFile(
String file,
String initialPage,
bool isHeadless,
) async {
final results = await serveWebBenchmark(
benchmarkAppDirectory: Directory('.'),
entryPoint: file,
initialPage: initialPage,
headless: isHeadless,
);

final resultFile = File(
file.replaceFirst('.dart', '_${formatDate(DateTime.now().toUtc())}.json'),
);

await resultFile.writeAsString(
const JsonEncoder.withIndent(' ').convert(results.toJson()),
);
}

String formatDate(DateTime date) {
return '${date.year}-${date.month}-${date.day}_${date.hour}-${date.minute}-${date.second}';
}

0 comments on commit e8b9fef

Please sign in to comment.