blob: 753eb70d1c97f8ddaa4d35b479bca05ab151a19d [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.6
import 'dart:io' as io;
import 'package:path/path.dart' as pathlib;
import 'package:web_driver_installer/chrome_driver_installer.dart';
import 'package:web_driver_installer/firefox_driver_installer.dart';
import 'package:web_driver_installer/safari_driver_runner.dart';
import 'package:yaml/yaml.dart';
import 'chrome_installer.dart';
import 'common.dart';
import 'environment.dart';
import 'utils.dart';
/// [DriverManager] implementation for Chrome.
///
/// This manager can be used for both macOS and Linux.
class ChromeDriverManager extends DriverManager {
/// Directory which contains the Chrome's major version.
///
/// On LUCI we are using the CIPD packages to control Chrome binaries we use.
/// There will be multiple CIPD packages loaded at the same time. Yaml file
/// `driver_version.yaml` contains the version number we want to use.
///
/// Initialized to the current first to avoid the `Non-nullable` error.
// TODO: https://github.com/flutter/flutter/issues/53179. Local integration
// tests are still using the system Chrome.
io.Directory _browserDriverDirWithVersion;
ChromeDriverManager(String browser) : super(browser) {
final io.File lockFile = io.File(pathlib.join(
environment.webUiRootDir.path, 'dev', 'browser_lock.yaml'));
final YamlMap _configuration =
loadYaml(lockFile.readAsStringSync()) as YamlMap;
final String requiredChromeDriverVersion =
_configuration['required_driver_version']['chrome'] as String;
print('INFO: Major version for Chrome Driver $requiredChromeDriverVersion');
_browserDriverDirWithVersion = io.Directory(pathlib.join(
environment.webUiDartToolDir.path,
'drivers',
browser,
requiredChromeDriverVersion,
'${browser}driver-${io.Platform.operatingSystem.toString()}'));
}
@override
Future<void> _installDriver() async {
if (_browserDriverDirWithVersion.existsSync()) {
_browserDriverDirWithVersion.deleteSync(recursive: true);
}
_browserDriverDirWithVersion.createSync(recursive: true);
temporaryDirectories.add(_drivers);
final io.Directory temp = io.Directory.current;
io.Directory.current = _browserDriverDirWithVersion;
try {
// TODO(nurhan): https://github.com/flutter/flutter/issues/53179
final String chromeDriverVersion = await queryChromeDriverVersion();
ChromeDriverInstaller chromeDriverInstaller =
ChromeDriverInstaller.withVersion(chromeDriverVersion);
await chromeDriverInstaller.install(alwaysInstall: true);
} finally {
io.Directory.current = temp;
}
}
/// Throw an error if driver directory does not exists.
///
/// Driver should already exist on LUCI as a CIPD package.
@override
Future<void> _verifyDriverForLUCI() {
if (!_browserDriverDirWithVersion.existsSync()) {
throw StateError('Failed to locate Chrome driver on LUCI on path:'
'${_browserDriverDirWithVersion.path}');
}
return Future<void>.value();
}
@override
Future<void> _startDriver() async {
await startProcess('./chromedriver/chromedriver', ['--port=4444'],
workingDirectory: _browserDriverDirWithVersion.path);
print('INFO: Driver started');
}
}
/// [DriverManager] implementation for Firefox.
///
/// This manager can be used for both macOS and Linux.
class FirefoxDriverManager extends DriverManager {
FirefoxDriverManager(String browser) : super(browser);
FirefoxDriverInstaller firefoxDriverInstaller =
FirefoxDriverInstaller(geckoDriverVersion: getLockedGeckoDriverVersion());
@override
Future<void> _installDriver() async {
if (_browserDriverDir.existsSync()) {
_browserDriverDir.deleteSync(recursive: true);
}
_browserDriverDir.createSync(recursive: true);
temporaryDirectories.add(_drivers);
final io.Directory temp = io.Directory.current;
io.Directory.current = _browserDriverDir;
try {
await firefoxDriverInstaller.install(alwaysInstall: false);
} finally {
io.Directory.current = temp;
}
}
/// Throw an error if driver directory does not exist.
///
/// Driver should already exist on LUCI as a CIPD package.
@override
Future<void> _verifyDriverForLUCI() {
if (!_browserDriverDir.existsSync()) {
throw StateError('Failed to locate Firefox driver on LUCI on path:'
'${_browserDriverDir.path}');
}
return Future<void>.value();
}
@override
Future<void> _startDriver() async {
await startProcess('./firefoxdriver/geckodriver', ['--port=4444'],
workingDirectory: _browserDriverDir.path);
print('INFO: Driver started');
}
/// Get the geckodriver version to be used with [FirefoxDriverInstaller].
///
/// For different versions of geckodriver. See:
/// https://github.com/mozilla/geckodriver/releases
static String getLockedGeckoDriverVersion() {
final YamlMap browserLock = BrowserLock.instance.configuration;
String geckoDriverReleaseVersion = browserLock['geckodriver'] as String;
return geckoDriverReleaseVersion;
}
}
/// [DriverManager] implementation for Safari.
///
/// This manager is will only be created/used for macOS.
class SafariDriverManager extends DriverManager {
SafariDriverManager(String browser) : super(browser);
@override
Future<void> _installDriver() {
// No-op.
// macOS comes with Safari Driver installed.
return new Future<void>.value();
}
@override
Future<void> _verifyDriverForLUCI() {
// No-op.
// macOS comes with Safari Driver installed.
return Future<void>.value();
}
@override
Future<void> _startDriver() async {
final SafariDriverRunner safariDriverRunner = SafariDriverRunner();
final io.Process process =
await safariDriverRunner.runDriver(version: 'system');
processesToCleanUp.add(process);
}
}
/// Abstract class for preparing the browser driver before running the integration
/// tests.
abstract class DriverManager {
/// Installation directory for browser's driver.
final io.Directory _browserDriverDir;
/// This is the parent directory for all drivers.
///
/// This directory is saved to [temporaryDirectories] and deleted before
/// tests shutdown.
final io.Directory _drivers;
DriverManager(String browser)
: this._browserDriverDir = io.Directory(pathlib.join(
environment.webUiDartToolDir.path,
'drivers',
browser,
'${browser}driver-${io.Platform.operatingSystem.toString()}')),
this._drivers = io.Directory(
pathlib.join(environment.webUiDartToolDir.path, 'drivers'));
Future<void> prepareDriver() async {
if (!isLuci) {
// LUCI installs driver from CIPD, so we skip installing it on LUCI.
await _installDriver();
} else {
await _verifyDriverForLUCI();
}
await _startDriver();
}
/// Always re-install since driver can change frequently.
/// It usually changes with each the browser version changes.
/// A better solution would be installing the browser and the driver at the
/// same time.
/// TODO(nurhan): https://github.com/flutter/flutter/issues/53179. Partly
// solved. Remaining local integration tests using the locked Chrome version.
Future<void> _installDriver();
Future<void> _verifyDriverForLUCI();
Future<void> _startDriver();
static DriverManager chooseDriver(String browser) {
if (browser == 'chrome') {
return ChromeDriverManager(browser);
} else if (browser == 'firefox') {
return FirefoxDriverManager(browser);
} else if (browser == 'safari' && io.Platform.isMacOS) {
return SafariDriverManager(browser);
} else {
throw StateError('Integration tests are only supported on Firefox, Chrome'
' and on Safari (running on macOS)');
}
}
}