mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

* Update project.pbxproj files to say Flutter rather than Chromium Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright. * Update the copyright notice checker to require a standard notice on all files * Update copyrights on Dart files. (This was a mechanical commit.) * Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine. Some were already marked "The Flutter Authors", not clear why. Their dates have been normalized. Some were missing the blank line after the license. Some were randomly different in trivial ways for no apparent reason (e.g. missing the trailing period). * Clean up the copyrights in non-Dart files. (Manual edits.) Also, make sure templates don't have copyrights. * Fix some more ORGANIZATIONNAMEs
451 lines
17 KiB
Dart
451 lines
17 KiB
Dart
// 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:collection';
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/base/common.dart';
|
|
import 'package:flutter_tools/src/base/context.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/base/utils.dart';
|
|
import 'package:flutter_tools/src/cache.dart';
|
|
import 'package:flutter_tools/src/dart/pub.dart';
|
|
import 'package:flutter_tools/src/reporting/reporting.dart';
|
|
|
|
import 'package:mockito/mockito.dart';
|
|
import 'package:process/process.dart';
|
|
import 'package:quiver/testing/async.dart';
|
|
|
|
import '../../src/common.dart';
|
|
import '../../src/context.dart';
|
|
import '../../src/mocks.dart' as mocks;
|
|
import '../../src/testbed.dart';
|
|
|
|
void main() {
|
|
setUpAll(() {
|
|
Cache.flutterRoot = getFlutterRoot();
|
|
});
|
|
|
|
testUsingContext('pub get 69', () async {
|
|
String error;
|
|
|
|
final MockProcessManager processMock = context.get<ProcessManager>() as MockProcessManager;
|
|
|
|
FakeAsync().run((FakeAsync time) {
|
|
expect(processMock.lastPubEnvironment, isNull);
|
|
expect(testLogger.statusText, '');
|
|
pub.get(context: PubContext.flutterTests, checkLastModified: false).then((void value) {
|
|
error = 'test completed unexpectedly';
|
|
}, onError: (dynamic thrownError) {
|
|
error = 'test failed unexpectedly: $thrownError';
|
|
});
|
|
time.elapse(const Duration(milliseconds: 500));
|
|
expect(testLogger.statusText,
|
|
'Running "flutter pub get" in /...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n',
|
|
);
|
|
expect(processMock.lastPubEnvironment, contains('flutter_cli:flutter_tests'));
|
|
expect(processMock.lastPubCache, isNull);
|
|
time.elapse(const Duration(milliseconds: 500));
|
|
expect(testLogger.statusText,
|
|
'Running "flutter pub get" in /...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n',
|
|
);
|
|
time.elapse(const Duration(seconds: 1));
|
|
expect(testLogger.statusText,
|
|
'Running "flutter pub get" in /...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n',
|
|
);
|
|
time.elapse(const Duration(seconds: 100)); // from t=0 to t=100
|
|
expect(testLogger.statusText,
|
|
'Running "flutter pub get" in /...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n' // at t=1
|
|
'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n' // at t=5
|
|
'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n' // at t=13
|
|
'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n' // at t=29
|
|
'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n', // at t=61
|
|
);
|
|
time.elapse(const Duration(seconds: 200)); // from t=0 to t=200
|
|
expect(testLogger.statusText,
|
|
'Running "flutter pub get" in /...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n'
|
|
'pub get failed (server unavailable) -- attempting retry 8 in 64 seconds...\n' // at t=39
|
|
'pub get failed (server unavailable) -- attempting retry 9 in 64 seconds...\n' // at t=103
|
|
'pub get failed (server unavailable) -- attempting retry 10 in 64 seconds...\n', // at t=167
|
|
);
|
|
});
|
|
expect(testLogger.errorText, isEmpty);
|
|
expect(error, isNull);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MockFileSystem(),
|
|
ProcessManager: () => MockProcessManager(69),
|
|
Platform: () => FakePlatform(
|
|
environment: UnmodifiableMapView<String, String>(<String, String>{}),
|
|
),
|
|
Pub: () => const Pub(),
|
|
});
|
|
|
|
testUsingContext('pub get 66 shows message from pub', () async {
|
|
try {
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: false);
|
|
throw AssertionError('pubGet did not fail');
|
|
} on ToolExit catch (error) {
|
|
expect(error.message, 'pub get failed (66; err3)');
|
|
}
|
|
expect(testLogger.statusText,
|
|
'Running "flutter pub get" in /...\n'
|
|
'out1\n'
|
|
'out2\n'
|
|
'out3\n'
|
|
);
|
|
expect(testLogger.errorText,
|
|
'err1\n'
|
|
'err2\n'
|
|
'err3\n'
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => MockProcessManager(66, stderr: 'err1\nerr2\nerr3\n', stdout: 'out1\nout2\nout3\n'),
|
|
FileSystem: () => MockFileSystem(),
|
|
Platform: () => FakePlatform(
|
|
environment: UnmodifiableMapView<String, String>(<String, String>{}),
|
|
),
|
|
Pub: () => const Pub(),
|
|
});
|
|
|
|
testUsingContext('pub cache in root is used', () async {
|
|
String error;
|
|
|
|
final MockProcessManager processMock = context.get<ProcessManager>() as MockProcessManager;
|
|
final MockFileSystem fsMock = context.get<FileSystem>() as MockFileSystem;
|
|
|
|
FakeAsync().run((FakeAsync time) {
|
|
MockDirectory.findCache = true;
|
|
expect(processMock.lastPubEnvironment, isNull);
|
|
expect(processMock.lastPubCache, isNull);
|
|
pub.get(context: PubContext.flutterTests, checkLastModified: false).then((void value) {
|
|
error = 'test completed unexpectedly';
|
|
}, onError: (dynamic thrownError) {
|
|
error = 'test failed unexpectedly: $thrownError';
|
|
});
|
|
time.elapse(const Duration(milliseconds: 500));
|
|
expect(processMock.lastPubCache, equals(fsMock.path.join(Cache.flutterRoot, '.pub-cache')));
|
|
expect(error, isNull);
|
|
});
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MockFileSystem(),
|
|
ProcessManager: () => MockProcessManager(69),
|
|
Platform: () => FakePlatform(
|
|
environment: UnmodifiableMapView<String, String>(<String, String>{}),
|
|
),
|
|
Pub: () => const Pub(),
|
|
});
|
|
|
|
testUsingContext('pub cache in environment is used', () async {
|
|
String error;
|
|
|
|
final MockProcessManager processMock = context.get<ProcessManager>() as MockProcessManager;
|
|
|
|
FakeAsync().run((FakeAsync time) {
|
|
MockDirectory.findCache = true;
|
|
expect(processMock.lastPubEnvironment, isNull);
|
|
expect(processMock.lastPubCache, isNull);
|
|
pub.get(context: PubContext.flutterTests, checkLastModified: false).then((void value) {
|
|
error = 'test completed unexpectedly';
|
|
}, onError: (dynamic thrownError) {
|
|
error = 'test failed unexpectedly: $thrownError';
|
|
});
|
|
time.elapse(const Duration(milliseconds: 500));
|
|
expect(processMock.lastPubCache, equals('custom/pub-cache/path'));
|
|
expect(error, isNull);
|
|
});
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MockFileSystem(),
|
|
ProcessManager: () => MockProcessManager(69),
|
|
Platform: () => FakePlatform(
|
|
environment: UnmodifiableMapView<String, String>(<String, String>{
|
|
'PUB_CACHE': 'custom/pub-cache/path',
|
|
}),
|
|
),
|
|
Pub: () => const Pub(),
|
|
});
|
|
|
|
testUsingContext('analytics sent on success', () async {
|
|
MockDirectory.findCache = true;
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: false);
|
|
verify(flutterUsage.sendEvent('pub-result', 'flutter-tests', label: 'success')).called(1);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MockFileSystem(),
|
|
ProcessManager: () => MockProcessManager(0),
|
|
Platform: () => FakePlatform(
|
|
environment: UnmodifiableMapView<String, String>(<String, String>{
|
|
'PUB_CACHE': 'custom/pub-cache/path',
|
|
}),
|
|
),
|
|
Usage: () => MockUsage(),
|
|
Pub: () => const Pub(),
|
|
});
|
|
|
|
testUsingContext('analytics sent on failure', () async {
|
|
MockDirectory.findCache = true;
|
|
try {
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: false);
|
|
} on ToolExit {
|
|
// Ignore.
|
|
}
|
|
verify(flutterUsage.sendEvent('pub-result', 'flutter-tests', label: 'failure')).called(1);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MockFileSystem(),
|
|
ProcessManager: () => MockProcessManager(1),
|
|
Platform: () => FakePlatform(
|
|
environment: UnmodifiableMapView<String, String>(<String, String>{
|
|
'PUB_CACHE': 'custom/pub-cache/path',
|
|
}),
|
|
),
|
|
Usage: () => MockUsage(),
|
|
Pub: () => const Pub(),
|
|
});
|
|
|
|
testUsingContext('analytics sent on failed version solve', () async {
|
|
MockDirectory.findCache = true;
|
|
try {
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: false);
|
|
} on ToolExit {
|
|
// Ignore.
|
|
}
|
|
verify(flutterUsage.sendEvent('pub-result', 'flutter-tests', label: 'version-solving-failed')).called(1);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MockFileSystem(),
|
|
ProcessManager: () => MockProcessManager(
|
|
1,
|
|
stderr: 'version solving failed',
|
|
),
|
|
Platform: () => FakePlatform(
|
|
environment: UnmodifiableMapView<String, String>(<String, String>{
|
|
'PUB_CACHE': 'custom/pub-cache/path',
|
|
}),
|
|
),
|
|
Usage: () => MockUsage(),
|
|
Pub: () => const Pub(),
|
|
});
|
|
|
|
test('Pub error handling', () async {
|
|
final MemoryFileSystem fileSystem = MemoryFileSystem();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>[
|
|
'/bin/cache/dart-sdk/bin/pub',
|
|
'--verbosity=warning',
|
|
'get',
|
|
'--no-precompile',
|
|
],
|
|
onRun: () {
|
|
fs.file('.packages')
|
|
..setLastModifiedSync(DateTime(2002));
|
|
}
|
|
),
|
|
const FakeCommand(
|
|
command: <String>[
|
|
'/bin/cache/dart-sdk/bin/pub',
|
|
'--verbosity=warning',
|
|
'get',
|
|
'--no-precompile',
|
|
],
|
|
),
|
|
FakeCommand(
|
|
command: const <String>[
|
|
'/bin/cache/dart-sdk/bin/pub',
|
|
'--verbosity=warning',
|
|
'get',
|
|
'--no-precompile',
|
|
],
|
|
onRun: () {
|
|
fs.file('pubspec.yaml')
|
|
..setLastModifiedSync(DateTime(2002));
|
|
}
|
|
),
|
|
const FakeCommand(
|
|
command: <String>[
|
|
'/bin/cache/dart-sdk/bin/pub',
|
|
'--verbosity=warning',
|
|
'get',
|
|
'--no-precompile',
|
|
],
|
|
),
|
|
]);
|
|
await Testbed().run(() async {
|
|
// the good scenario: .packages is old, pub updates the file.
|
|
fs.file('.packages')
|
|
..createSync()
|
|
..setLastModifiedSync(DateTime(2000));
|
|
fs.file('pubspec.yaml')
|
|
..createSync()
|
|
..setLastModifiedSync(DateTime(2001));
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub sets date of .packages to 2002
|
|
expect(testLogger.statusText, 'Running "flutter pub get" in /...\n');
|
|
expect(testLogger.errorText, isEmpty);
|
|
expect(fs.file('pubspec.yaml').lastModifiedSync(), DateTime(2001)); // because nothing should touch it
|
|
expect(fs.file('.packages').lastModifiedSync(), isNot(DateTime(2000))); // because pub changes it to 2002
|
|
expect(fs.file('.packages').lastModifiedSync(), isNot(DateTime(2002))); // because we set the timestamp again after pub
|
|
testLogger.clear();
|
|
// bad scenario 1: pub doesn't update file; doesn't matter, because we do instead
|
|
fs.file('.packages')
|
|
..setLastModifiedSync(DateTime(2000));
|
|
fs.file('pubspec.yaml')
|
|
..setLastModifiedSync(DateTime(2001));
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub does nothing
|
|
expect(testLogger.statusText, 'Running "flutter pub get" in /...\n');
|
|
expect(testLogger.errorText, isEmpty);
|
|
expect(fs.file('pubspec.yaml').lastModifiedSync(), DateTime(2001)); // because nothing should touch it
|
|
expect(fs.file('.packages').lastModifiedSync(), isNot(DateTime(2000))); // because we set the timestamp
|
|
expect(fs.file('.packages').lastModifiedSync(), isNot(DateTime(2002))); // just in case FakeProcessManager is buggy
|
|
testLogger.clear();
|
|
// bad scenario 2: pub changes pubspec.yaml instead
|
|
fs.file('.packages')
|
|
..setLastModifiedSync(DateTime(2000));
|
|
fs.file('pubspec.yaml')
|
|
..setLastModifiedSync(DateTime(2001));
|
|
try {
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: true);
|
|
expect(true, isFalse, reason: 'pub.get did not throw');
|
|
} catch (error) {
|
|
expect(error, isInstanceOf<Exception>());
|
|
expect(error.message, '/: unexpected concurrent modification of pubspec.yaml while running pub.');
|
|
}
|
|
expect(testLogger.statusText, 'Running "flutter pub get" in /...\n');
|
|
expect(testLogger.errorText, isEmpty);
|
|
expect(fs.file('pubspec.yaml').lastModifiedSync(), DateTime(2002)); // because fake pub above touched it
|
|
expect(fs.file('.packages').lastModifiedSync(), DateTime(2000)); // because nothing touched it
|
|
// bad scenario 3: pubspec.yaml was created in the future
|
|
fs.file('.packages')
|
|
..setLastModifiedSync(DateTime(2000));
|
|
fs.file('pubspec.yaml')
|
|
..setLastModifiedSync(DateTime(9999));
|
|
assert(DateTime(9999).isAfter(DateTime.now()));
|
|
await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub does nothing
|
|
expect(testLogger.statusText, contains('Running "flutter pub get" in /...\n'));
|
|
expect(testLogger.errorText, startsWith(
|
|
'Warning: File "/pubspec.yaml" was created in the future. Optimizations that rely on '
|
|
'comparing time stamps will be unreliable. Check your system clock for accuracy.\n'
|
|
'The timestamp was:'
|
|
));
|
|
testLogger.clear();
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
Pub: () => const Pub(),
|
|
Platform: () => FakePlatform(
|
|
operatingSystem: 'linux', // so that the command executed is consistent
|
|
environment: <String, String>{},
|
|
),
|
|
BotDetector: () => const BotDetectorAlwaysNo(), // so that the test never adds --trace to the pub command
|
|
});
|
|
});
|
|
}
|
|
|
|
class BotDetectorAlwaysNo implements BotDetector {
|
|
const BotDetectorAlwaysNo();
|
|
@override
|
|
bool get isRunningOnBot => false;
|
|
}
|
|
|
|
typedef StartCallback = void Function(List<dynamic> command);
|
|
|
|
class MockProcessManager implements ProcessManager {
|
|
MockProcessManager(this.fakeExitCode, {
|
|
this.stdout = '',
|
|
this.stderr = '',
|
|
});
|
|
|
|
final int fakeExitCode;
|
|
final String stdout;
|
|
final String stderr;
|
|
|
|
String lastPubEnvironment;
|
|
String lastPubCache;
|
|
|
|
@override
|
|
Future<Process> start(
|
|
List<dynamic> command, {
|
|
String workingDirectory,
|
|
Map<String, String> environment,
|
|
bool includeParentEnvironment = true,
|
|
bool runInShell = false,
|
|
ProcessStartMode mode = ProcessStartMode.normal,
|
|
}) {
|
|
lastPubEnvironment = environment['PUB_ENVIRONMENT'];
|
|
lastPubCache = environment['PUB_CACHE'];
|
|
return Future<Process>.value(mocks.createMockProcess(
|
|
exitCode: fakeExitCode,
|
|
stdout: stdout,
|
|
stderr: stderr,
|
|
));
|
|
}
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => null;
|
|
}
|
|
|
|
class MockFileSystem extends ForwardingFileSystem {
|
|
MockFileSystem() : super(MemoryFileSystem());
|
|
|
|
@override
|
|
File file(dynamic path) {
|
|
return MockFile();
|
|
}
|
|
|
|
@override
|
|
Directory directory(dynamic path) {
|
|
return MockDirectory(path as String);
|
|
}
|
|
}
|
|
|
|
class MockFile implements File {
|
|
@override
|
|
Future<RandomAccessFile> open({ FileMode mode = FileMode.read }) async {
|
|
return MockRandomAccessFile();
|
|
}
|
|
|
|
@override
|
|
bool existsSync() => true;
|
|
|
|
@override
|
|
DateTime lastModifiedSync() => DateTime(0);
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => null;
|
|
}
|
|
|
|
class MockDirectory implements Directory {
|
|
MockDirectory(this.path);
|
|
|
|
@override
|
|
final String path;
|
|
|
|
static bool findCache = false;
|
|
|
|
@override
|
|
bool existsSync() => findCache && path.endsWith('.pub-cache');
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => null;
|
|
}
|
|
|
|
class MockRandomAccessFile extends Mock implements RandomAccessFile {}
|
|
|
|
class MockUsage extends Mock implements Usage {}
|