blob: 1fe6763197fd9fbb1efc2206973a18df20571dbb [file] [log] [blame]
// Copyright 2014 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.
import 'dart:async';
import 'dart:io';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/doctor_validator.dart';
import 'package:flutter_tools/src/http_host_validator.dart';
import '../../src/common.dart';
import '../../src/fake_http_client.dart';
import '../../src/fakes.dart';
// The environment variables used to override some URLs
const String kTestEnvPubHost = 'https://pub.flutter-io.cn';
const String kTestEnvGCloudHost = 'https://storage.flutter-io.cn';
const Map<String, String> kTestEnvironment = <String, String>{
'PUB_HOSTED_URL': kTestEnvPubHost,
'FLUTTER_STORAGE_BASE_URL': kTestEnvGCloudHost,
'FLUTTER_DOCTOR_HOST_TIMEOUT': '1',
};
void main() {
group('http host validator', () {
const List<String> osTested = <String>['windows', 'macos', 'linux'];
group('no env variables', () {
testWithoutContext('all http hosts are available', () async {
final FakeHttpClient mockClient = FakeHttpClient.any();
// Run the check for all operating systems one by one
for(final String os in osTested) {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(operatingSystem: os),
featureFlags: TestFeatureFlags(),
httpClient: mockClient,
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.installed result
expect(result.type, equals(ValidationType.installed));
}
});
testWithoutContext('all http hosts are not available', () async {
// Run the check for all operating systems one by one
for(final String os in osTested) {
final Platform platform = FakePlatform(operatingSystem: os);
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: platform,
featureFlags: TestFeatureFlags(),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kgCloudHttpHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
FakeRequest(Uri.parse(androidRequiredHttpHosts(platform)[0]), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
FakeRequest(Uri.parse(kPubDevHttpHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
FakeRequest(Uri.parse(macOSRequiredHttpHosts[0]), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.notAvailable result
expect(result.type, equals(ValidationType.notAvailable));
}
});
testWithoutContext('one http host is not available', () async {
// Run the check for all operating systems one by one
for(final String os in osTested) {
final Platform platform = FakePlatform(operatingSystem: os);
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: platform,
featureFlags: TestFeatureFlags(),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kgCloudHttpHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
FakeRequest(Uri.parse(androidRequiredHttpHosts(platform)[0]), method: HttpMethod.head),
FakeRequest(Uri.parse(kPubDevHttpHost), method: HttpMethod.head),
FakeRequest(Uri.parse(macOSRequiredHttpHosts[0]), method: HttpMethod.head),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.partial result
expect(result.type, equals(ValidationType.partial));
}
});
});
group('with env variables', () {
testWithoutContext('all http hosts are available', () async {
final FakeHttpClient mockClient = FakeHttpClient.any();
// Run the check for all operating systems one by one
for(final String os in osTested) {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(operatingSystem: os, environment: kTestEnvironment),
featureFlags: TestFeatureFlags(),
httpClient: mockClient,
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.installed result
expect(result.type, equals(ValidationType.installed));
}
});
testWithoutContext('all http hosts are not available', () async {
// Run the check for all operating systems one by one
for(final String os in osTested) {
final Platform platform = FakePlatform(operatingSystem: os, environment: kTestEnvironment);
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: platform,
featureFlags: TestFeatureFlags(),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kTestEnvGCloudHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
FakeRequest(Uri.parse(kTestEnvPubHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
FakeRequest(Uri.parse(macOSRequiredHttpHosts[0]), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.notAvailable result
expect(result.type, equals(ValidationType.notAvailable));
}
});
testWithoutContext('one http host is not available', () async {
// Run the check for all operating systems one by one
for(final String os in osTested) {
final Platform platform = FakePlatform(operatingSystem: os, environment: kTestEnvironment);
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: platform,
featureFlags: TestFeatureFlags(),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kTestEnvGCloudHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2)),
FakeRequest(Uri.parse(kTestEnvPubHost), method: HttpMethod.head),
FakeRequest(Uri.parse(macOSRequiredHttpHosts[0]), method: HttpMethod.head),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.partial result
expect(result.type, equals(ValidationType.partial));
}
});
testWithoutContext('does not throw on invalid user-defined timeout', () async {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(
environment: <String,String> {
'PUB_HOSTED_URL': kTestEnvPubHost,
'FLUTTER_STORAGE_BASE_URL': kTestEnvGCloudHost,
'FLUTTER_DOCTOR_HOST_TIMEOUT' : 'deadbeef',
},
),
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
httpClient: FakeHttpClient.any(),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
expect(result.type, equals(ValidationType.notAvailable));
expect(
result.messages,
contains(const ValidationMessage.error(
'HTTP host "$kTestEnvPubHost" is not reachable. '
'Reason: The value of FLUTTER_DOCTOR_HOST_TIMEOUT(deadbeef) is not a valid duration in seconds',
)),
);
});
testWithoutContext('does not throw on unparseable user-defined host uri', () async {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(
environment: <String,String> {
'PUB_HOSTED_URL': '::Not A Uri::',
'FLUTTER_STORAGE_BASE_URL': kTestEnvGCloudHost,
'FLUTTER_DOCTOR_HOST_TIMEOUT' : '1',
},
),
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
httpClient: FakeHttpClient.any(),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
expect(result.type, equals(ValidationType.partial));
expect(
result.messages,
contains(const ValidationMessage.error(
'HTTP host "::Not A Uri::" is not reachable. '
'Reason: The value of PUB_HOSTED_URL(::Not A Uri::) could not be parsed as a valid url',
)),
);
});
testWithoutContext('does not throw on invalid user-defined host', () async {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(
environment: <String,String> {
'PUB_HOSTED_URL': kTestEnvPubHost,
'FLUTTER_STORAGE_BASE_URL': '',
'FLUTTER_DOCTOR_HOST_TIMEOUT' : '1',
},
),
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
httpClient: FakeHttpClient.any(),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
expect(result.type, equals(ValidationType.partial));
expect(
result.messages,
contains(const ValidationMessage.error(
'HTTP host "" is not reachable. '
'Reason: The value of FLUTTER_STORAGE_BASE_URL() is not a valid host',
)),
);
});
});
group('specific os disabled', () {
testWithoutContext('all http hosts are available - android disabled', () async {
// Run the check for all operating systems one by one
for(final String os in osTested) {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(operatingSystem: os),
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kgCloudHttpHost), method: HttpMethod.head),
FakeRequest(Uri.parse(kPubDevHttpHost), method: HttpMethod.head),
FakeRequest(Uri.parse(macOSRequiredHttpHosts[0]), method: HttpMethod.head),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.installed result
expect(result.type, equals(ValidationType.installed));
}
});
testWithoutContext('all http hosts are available - iOS disabled', () async {
// Run the check for all operating systems one by one
for(final String os in osTested) {
final Platform platform = FakePlatform(operatingSystem: os);
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: platform,
featureFlags: TestFeatureFlags(isIOSEnabled: false),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kgCloudHttpHost), method: HttpMethod.head),
FakeRequest(Uri.parse(kPubDevHttpHost), method: HttpMethod.head),
FakeRequest(Uri.parse(androidRequiredHttpHosts(platform)[0]), method: HttpMethod.head),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.installed result
expect(result.type, equals(ValidationType.installed));
}
});
testWithoutContext('all http hosts are available - android, iOS disabled', () async {
// Run the check for all operating systems one by one
for(final String os in osTested) {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(operatingSystem: os),
featureFlags: TestFeatureFlags(isAndroidEnabled: false, isIOSEnabled: false),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kgCloudHttpHost), method: HttpMethod.head),
FakeRequest(Uri.parse(kPubDevHttpHost), method: HttpMethod.head),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Check for a ValidationType.installed result
expect(result.type, equals(ValidationType.installed));
}
});
});
});
testWithoutContext('Does not throw on HandshakeException', () async {
const String handshakeMessage = '''
Handshake error in client (OS Error:
BLOCK_TYPE_IS_NOT_01(../../third_party/boringssl/src/crypto/fipsmodule/rsa/padding.c:108)
PADDING_CHECK_FAILED(../../third_party/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c:676)
public key routines(../../third_party/boringssl/src/crypto/x509/a_verify.c:108)
CERTIFICATE_VERIFY_FAILED: certificate signature failure(../../third_party/boringssl/src/ssl/handshake.cc:393))
''';
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(environment: kTestEnvironment),
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(
Uri.parse(kTestEnvPubHost),
method: HttpMethod.head,
responseError: const HandshakeException(handshakeMessage),
),
FakeRequest(Uri.parse(kTestEnvGCloudHost), method: HttpMethod.head),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
expect(
result.messages.first,
isA<ValidationMessage>().having(
(ValidationMessage msg) => msg.message,
'message',
contains(handshakeMessage),
),
);
});
testWithoutContext('Http host validator timeout message includes timeout duration.', () async {
final HttpHostValidator httpHostValidator = HttpHostValidator(
platform: FakePlatform(environment: kTestEnvironment),
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse(kTestEnvPubHost), method: HttpMethod.head, responseError: TimeoutException('Timeout error')),
FakeRequest(Uri.parse(kTestEnvGCloudHost), method: HttpMethod.head),
]),
);
// Run the validation check and get the results
final ValidationResult result = await httpHostValidator.validate();
// Timeout duration for tests is set to 1 second
expect(
result.messages,
contains(const ValidationMessage.error('HTTP host "$kTestEnvPubHost" is not reachable. Reason: Failed to connect to host in 1 second')),
);
});
}