Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit deef266

Browse files
author
Nurhan Turgut
authored
Run integration tests on ci (#17598)
* changing felt script to fetch flutter * changing the clone_flutter.sh code * running integration tests with felt on cirrus. fetch framework in CI (not in local). * only run cirrus tests on chrome. fix a comma in the flutter command path * adding comments to public flags * use local engine parameter for flutter pub get * change flutter executable used for flutter drive command * fix a cleanup issue. address comments. add toolException. enable web in flutter * address reviwer comments. fix issue with local-engine * address reviwer comments. fix issue with local-engine * using engine/flutter/.dart_tools as clone directory. enabling clone script for local usage * clean flutter repo with felt clean. add a flag to skip cloning the repo. always clone the repo even for local development, unless this flag is set * fixing typos. updating readme for the new flag. * fix directory error * addressing reviewer comments
1 parent 85b9c9c commit deef266

9 files changed

+165
-39
lines changed

lib/web_ui/dev/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,18 @@ To run unit tests only:
4343
felt test --unit-tests-only
4444
```
4545

46-
To run integration tests only. For now these tests are only available on Chrome Desktop browsers.
46+
To run integration tests only. For now these tests are only available on Chrome Desktop browsers. These tests will fetch the flutter repository for using `flutter drive` and `flutter pub get` commands. The repository will be synced to the youngest commit older than the engine commit.
4747

4848
```
4949
felt test --integration-tests-only
5050
```
5151

52+
To skip cloning the flutter repository use the following flag. This flag can save internet bandwidth. However use with caution. Note the tests results will not be consistent with CIs when this flag is set. flutter command should be set in the PATH for this flag to be useful. This flag can also be used to test local Flutter changes.
53+
54+
```
55+
felt test --integration-tests-only --use-system-flutter
56+
```
57+
5258
To run tests on Firefox (this will work only on a Linux device):
5359

5460
```

lib/web_ui/dev/clean.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import 'utils.dart';
1515
class CleanCommand extends Command<bool> with ArgUtils {
1616
CleanCommand() {
1717
argParser
18+
..addFlag(
19+
'flutter',
20+
defaultsTo: true,
21+
help: 'Cleans up the .dart_tool directory under engine/src/flutter.',
22+
)
1823
..addFlag(
1924
'ninja',
2025
defaultsTo: false,
@@ -27,6 +32,8 @@ class CleanCommand extends Command<bool> with ArgUtils {
2732

2833
bool get _alsoCleanNinja => boolArg('ninja');
2934

35+
bool get _alsoCleanFlutterRepo => boolArg('flutter');
36+
3037
@override
3138
String get description => 'Deletes build caches and artifacts.';
3239

@@ -48,6 +55,8 @@ class CleanCommand extends Command<bool> with ArgUtils {
4855
...fontFiles,
4956
if (_alsoCleanNinja)
5057
environment.outDir,
58+
if(_alsoCleanFlutterRepo)
59+
environment.engineDartToolDir,
5160
];
5261

5362
await Future.wait(

lib/web_ui/dev/common.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,12 @@ class DevNull implements StringSink {
231231
void writeln([Object obj = ""]) {}
232232
}
233233

234+
/// Whether the felt command is running on Cirrus CI.
234235
bool get isCirrus => io.Platform.environment['CIRRUS_CI'] == 'true';
236+
237+
/// Whether the felt command is running on LUCI.
238+
bool get isLuci => io.Platform.environment['LUCI_CONTEXT'] != null;
239+
240+
/// Whether the felt command is running on one of the Continuous Integration
241+
/// environements.
242+
bool get isCi => isCirrus || isLuci;

lib/web_ui/dev/environment.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Environment {
2020
factory Environment() {
2121
final io.File self = io.File.fromUri(io.Platform.script);
2222
final io.Directory engineSrcDir = self.parent.parent.parent.parent.parent;
23+
final io.Directory engineToolsDir = io.Directory(pathlib.join(engineSrcDir.path, 'flutter', 'tools'));
2324
final io.Directory outDir = io.Directory(pathlib.join(engineSrcDir.path, 'out'));
2425
final io.Directory hostDebugUnoptDir = io.Directory(pathlib.join(outDir.path, 'host_debug_unopt'));
2526
final io.Directory dartSdkDir = io.Directory(pathlib.join(hostDebugUnoptDir.path, 'dart-sdk'));
@@ -36,6 +37,7 @@ class Environment {
3637
self: self,
3738
webUiRootDir: webUiRootDir,
3839
engineSrcDir: engineSrcDir,
40+
engineToolsDir: engineToolsDir,
3941
integrationTestsDir: integrationTestsDir,
4042
outDir: outDir,
4143
hostDebugUnoptDir: hostDebugUnoptDir,
@@ -47,6 +49,7 @@ class Environment {
4749
this.self,
4850
this.webUiRootDir,
4951
this.engineSrcDir,
52+
this.engineToolsDir,
5053
this.integrationTestsDir,
5154
this.outDir,
5255
this.hostDebugUnoptDir,
@@ -62,6 +65,9 @@ class Environment {
6265
/// Path to the engine's "src" directory.
6366
final io.Directory engineSrcDir;
6467

68+
/// Path to the engine's "tools" directory.
69+
final io.Directory engineToolsDir;
70+
6571
/// Path to the web integration tests.
6672
final io.Directory integrationTestsDir;
6773

@@ -112,6 +118,16 @@ class Environment {
112118
'.dart_tool',
113119
));
114120

121+
/// Path to the ".dart_tool" directory living under `engine/src/flutter`.
122+
///
123+
/// This is a designated area for tool downloads which can be used by
124+
/// multiple platforms. For exampe: Flutter repo for e2e tests.
125+
io.Directory get engineDartToolDir => io.Directory(pathlib.join(
126+
engineSrcDir.path,
127+
'flutter',
128+
'.dart_tool',
129+
));
130+
115131
/// Path to the "dev" directory containing engine developer tools and
116132
/// configuration files.
117133
io.Directory get webUiDevDir => io.Directory(pathlib.join(
@@ -124,4 +140,23 @@ class Environment {
124140
webUiDartToolDir.path,
125141
'goldens',
126142
));
143+
144+
/// Path to the script that clones the Flutter repo.
145+
io.File get cloneFlutterScript => io.File(pathlib.join(
146+
engineToolsDir.path,
147+
'clone_flutter.sh',
148+
));
149+
150+
/// Path to flutter.
151+
///
152+
/// For example, this can be used to run `flutter pub get`.
153+
///
154+
/// Only use [cloneFlutterScript] to clone flutter to the engine build.
155+
io.File get flutterCommand => io.File(pathlib.join(
156+
engineDartToolDir.path,
157+
'flutter',
158+
'bin',
159+
'flutter',
160+
));
161+
127162
}

lib/web_ui/dev/felt.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ void main(List<String> args) async {
3737
final bool result = (await runner.run(args)) as bool;
3838
if (result == false) {
3939
print('Sub-command returned false: `${args.join(' ')}`');
40-
await cleanup();
4140
exitCode = 1;
4241
}
4342
} on UsageException catch (e) {

lib/web_ui/dev/integration_tests_manager.dart

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import 'package:path/path.dart' as pathlib;
77
import 'package:web_driver_installer/chrome_driver_installer.dart';
88

99
import 'chrome_installer.dart';
10-
import 'common.dart';
1110
import 'environment.dart';
11+
import 'exceptions.dart';
1212
import 'utils.dart';
1313

1414
class IntegrationTestsManager {
@@ -29,7 +29,9 @@ class IntegrationTestsManager {
2929
/// tests shutdown.
3030
final io.Directory _drivers;
3131

32-
IntegrationTestsManager(this._browser)
32+
final bool _useSystemFlutter;
33+
34+
IntegrationTestsManager(this._browser, this._useSystemFlutter)
3335
: this._browserDriverDir = io.Directory(pathlib.join(
3436
environment.webUiDartToolDir.path, 'drivers', _browser)),
3537
this._drivers = io.Directory(
@@ -46,37 +48,49 @@ class IntegrationTestsManager {
4648
}
4749
}
4850

49-
Future<bool> _runPubGet(String workingDirectory) async {
50-
final String executable = isCirrus ? environment.pubExecutable : 'flutter';
51-
final List<String> arguments = isCirrus
52-
? <String>[
53-
'get',
54-
]
55-
: <String>[
56-
'pub',
57-
'get',
58-
];
51+
Future<void> _runPubGet(String workingDirectory) async {
52+
if (!_useSystemFlutter) {
53+
await _cloneFlutterRepo();
54+
await _enableWeb(workingDirectory);
55+
}
56+
await runFlutter(workingDirectory, <String>['pub', 'get'],
57+
useSystemFlutter: _useSystemFlutter);
58+
}
59+
60+
/// Clone flutter repository, use the youngest commit older than the engine
61+
/// commit.
62+
///
63+
/// Use engine/src/flutter/.dart_tools to clone the Flutter repo.
64+
/// TODO(nurhan): Use git pull instead if repo exists.
65+
Future<void> _cloneFlutterRepo() async {
66+
// Delete directory if exists.
67+
if (environment.engineDartToolDir.existsSync()) {
68+
environment.engineDartToolDir.deleteSync();
69+
}
70+
environment.engineDartToolDir.createSync();
71+
5972
final int exitCode = await runProcess(
60-
executable,
61-
arguments,
62-
workingDirectory: workingDirectory,
73+
environment.cloneFlutterScript.path,
74+
<String>[
75+
environment.engineDartToolDir.path,
76+
],
77+
workingDirectory: environment.webUiRootDir.path,
6378
);
6479

6580
if (exitCode != 0) {
66-
io.stderr.writeln(
67-
'ERROR: Failed to run pub get. Exited with exit code $exitCode');
68-
return false;
69-
} else {
70-
return true;
81+
throw ToolException('ERROR: Failed to clone flutter repo. Exited with '
82+
'exit code $exitCode');
7183
}
7284
}
7385

86+
Future<void> _enableWeb(String workingDirectory) async {
87+
await runFlutter(workingDirectory, <String>['config', '--enable-web'],
88+
useSystemFlutter: _useSystemFlutter);
89+
}
90+
7491
void _runDriver() async {
75-
startProcess(
76-
'./chromedriver/chromedriver',
77-
['--port=4444'],
78-
workingDirectory: io.Directory.current.path
79-
);
92+
startProcess('./chromedriver/chromedriver', ['--port=4444'],
93+
workingDirectory: io.Directory.current.path);
8094
print('INFO: Driver started');
8195
}
8296

@@ -175,8 +189,10 @@ class IntegrationTestsManager {
175189

176190
Future<bool> _runTestsInProfileMode(
177191
io.Directory directory, String testName) async {
192+
final String executable =
193+
_useSystemFlutter ? 'flutter' : environment.flutterCommand.path;
178194
final int exitCode = await runProcess(
179-
'flutter',
195+
executable,
180196
<String>[
181197
'drive',
182198
'--target=test_driver/${testName}',

lib/web_ui/dev/test_runner.dart

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_im
1515
import 'package:test_core/src/executable.dart'
1616
as test; // ignore: implementation_imports
1717

18+
import 'environment.dart';
1819
import 'exceptions.dart';
1920
import 'integration_tests_manager.dart';
2021
import 'supported_browsers.dart';
2122
import 'test_platform.dart';
22-
import 'environment.dart';
2323
import 'utils.dart';
2424

2525
/// The type of tests requested by the tool user.
@@ -56,6 +56,18 @@ class TestCommand extends Command<bool> with ArgUtils {
5656
'at the same time. If this flag is set, only run the integration '
5757
'tests.',
5858
)
59+
..addFlag('use-system-flutter',
60+
defaultsTo: false,
61+
help: 'integration tests are using flutter repository for various tasks'
62+
', such as flutter drive, flutter pub get. If this flag is set, felt '
63+
'will use flutter command without cloning the repository. This flag '
64+
'can save internet bandwidth. However use with caution. Note that '
65+
'since flutter repo is always synced to youngest commit older than '
66+
'the engine commit for the tests running in CI, the tests results '
67+
'won\'t be consistent with CIs when this flag is set. flutter '
68+
'command should be set in the PATH for this flag to be useful.'
69+
'This flag can also be used to test local Flutter changes.'
70+
)
5971
..addFlag(
6072
'update-screenshot-goldens',
6173
defaultsTo: false,
@@ -117,7 +129,8 @@ class TestCommand extends Command<bool> with ArgUtils {
117129
return runIntegrationTests();
118130
case TestTypesRequested.all:
119131
// TODO(nurhan): https://github.com/flutter/flutter/issues/53322
120-
if (runAllTests) {
132+
// TODO(nurhan): Expand browser matrix for felt integration tests.
133+
if (runAllTests && isChrome) {
121134
bool integrationTestResult = await runIntegrationTests();
122135
bool unitTestResult = await runUnitTests();
123136
if (integrationTestResult != unitTestResult) {
@@ -134,11 +147,11 @@ class TestCommand extends Command<bool> with ArgUtils {
134147

135148
Future<bool> runIntegrationTests() async {
136149
// TODO(nurhan): https://github.com/flutter/flutter/issues/52983
137-
if (io.Platform.environment['LUCI_CONTEXT'] != null || isCirrus) {
150+
if (io.Platform.environment['LUCI_CONTEXT'] != null) {
138151
return true;
139152
}
140153

141-
return IntegrationTestsManager(browser).runTests();
154+
return IntegrationTestsManager(browser, useSystemFlutter).runTests();
142155
}
143156

144157
Future<bool> runUnitTests() async {
@@ -205,6 +218,11 @@ class TestCommand extends Command<bool> with ArgUtils {
205218
/// Whether [browser] is set to "chrome".
206219
bool get isChrome => browser == 'chrome';
207220

221+
/// Use system flutter instead of cloning the repository.
222+
///
223+
/// Read the flag help for more details. Uses PATH to locate flutter.
224+
bool get useSystemFlutter => boolArg('use-system-flutter');
225+
208226
/// When running screenshot tests writes them to the file system into
209227
/// ".dart_tool/goldens".
210228
bool get doUpdateScreenshotGoldens => boolArg('update-screenshot-goldens');

lib/web_ui/dev/utils.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:meta/meta.dart';
1111
import 'package:path/path.dart' as path;
1212

1313
import 'environment.dart';
14+
import 'exceptions.dart';
1415

1516
class FilePath {
1617
FilePath.fromCwd(String relativePath)
@@ -107,6 +108,26 @@ Future<String> evalProcess(
107108
return result.stdout as String;
108109
}
109110

111+
Future<void> runFlutter(
112+
String workingDirectory,
113+
List<String> arguments, {
114+
bool useSystemFlutter = false,
115+
}) async {
116+
final String executable =
117+
useSystemFlutter ? 'flutter' : environment.flutterCommand.path;
118+
arguments.add('--local-engine=host_debug_unopt');
119+
final int exitCode = await runProcess(
120+
executable,
121+
arguments,
122+
workingDirectory: workingDirectory,
123+
);
124+
125+
if (exitCode != 0) {
126+
throw ToolException('ERROR: Failed to run $executable with '
127+
'arguments ${arguments.toString()}. Exited with exit code $exitCode');
128+
}
129+
}
130+
110131
@immutable
111132
class ProcessException implements Exception {
112133
ProcessException({

0 commit comments

Comments
 (0)