Unverified Commit 10a7c9ba authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Unify analysis options (#108462)

parent 8a8ed75d
...@@ -99,7 +99,7 @@ linter: ...@@ -99,7 +99,7 @@ linter:
# - conditional_uri_does_not_exist # not yet tested # - conditional_uri_does_not_exist # not yet tested
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally - control_flow_in_finally
# - curly_braces_in_flow_control_structures # not required by flutter style, but developer-facing code should have this on - curly_braces_in_flow_control_structures
- depend_on_referenced_packages - depend_on_referenced_packages
- deprecated_consistency - deprecated_consistency
# - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib) # - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib)
......
...@@ -113,10 +113,11 @@ class ComplexLayoutState extends State<ComplexLayout> { ...@@ -113,10 +113,11 @@ class ComplexLayoutState extends State<ComplexLayout> {
key: const Key('complex-scroll'), // this key is used by the driver test key: const Key('complex-scroll'), // this key is used by the driver test
controller: ScrollController(), // So that the scroll offset can be tracked controller: ScrollController(), // So that the scroll offset can be tracked
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
if (index.isEven) if (index.isEven) {
return FancyImageItem(index, key: PageStorageKey<int>(index)); return FancyImageItem(index, key: PageStorageKey<int>(index));
else } else {
return FancyGalleryItem(index, key: PageStorageKey<int>(index)); return FancyGalleryItem(index, key: PageStorageKey<int>(index));
}
}, },
), ),
), ),
......
...@@ -239,8 +239,9 @@ Map<String, dynamic> scrollSummary( ...@@ -239,8 +239,9 @@ Map<String, dynamic> scrollSummary(
// //
final double absJerk = (scrollOffset[i-1] + scrollOffset[i+1] - 2*scrollOffset[i]).abs(); final double absJerk = (scrollOffset[i-1] + scrollOffset[i+1] - 2*scrollOffset[i]).abs();
absJerkAvg += absJerk; absJerkAvg += absJerk;
if (absJerk > 0.5) if (absJerk > 0.5) {
jankyCount += 1; jankyCount += 1;
}
} }
// expect(lostFrame < 0.1 * frameTimestamp.length, true); // expect(lostFrame < 0.1 * frameTimestamp.length, true);
absJerkAvg /= frameTimestamp.length - lostFrame; absJerkAvg /= frameTimestamp.length - lostFrame;
......
...@@ -18,8 +18,9 @@ void main() { ...@@ -18,8 +18,9 @@ void main() {
}); });
tearDownAll(() async { tearDownAll(() async {
if (driver != null) if (driver != null) {
driver.close(); driver.close();
}
}); });
Future<void> testScrollPerf(String listKey, String summaryName) async { Future<void> testScrollPerf(String listKey, String summaryName) async {
......
...@@ -19,8 +19,9 @@ void main() { ...@@ -19,8 +19,9 @@ void main() {
}); });
tearDownAll(() async { tearDownAll(() async {
if (driver != null) if (driver != null) {
driver.close(); driver.close();
}
}); });
test('initial tree creation', () async { test('initial tree creation', () async {
...@@ -34,8 +35,9 @@ void main() { ...@@ -34,8 +35,9 @@ void main() {
}); });
final Iterable<TimelineEvent>? semanticsEvents = timeline.events?.where((TimelineEvent event) => event.name == 'SEMANTICS'); final Iterable<TimelineEvent>? semanticsEvents = timeline.events?.where((TimelineEvent event) => event.name == 'SEMANTICS');
if (semanticsEvents?.length != 2) if (semanticsEvents?.length != 2) {
fail('Expected exactly two "SEMANTICS" events, got ${semanticsEvents?.length}:\n$semanticsEvents'); fail('Expected exactly two "SEMANTICS" events, got ${semanticsEvents?.length}:\n$semanticsEvents');
}
final Duration semanticsTreeCreation = Duration(microseconds: semanticsEvents!.last.timestampMicros! - semanticsEvents.first.timestampMicros!); final Duration semanticsTreeCreation = Duration(microseconds: semanticsEvents!.last.timestampMicros! - semanticsEvents.first.timestampMicros!);
final String jsonEncoded = json.encode(<String, dynamic>{'initialSemanticsTreeCreation': semanticsTreeCreation.inMilliseconds}); final String jsonEncoded = json.encode(<String, dynamic>{'initialSemanticsTreeCreation': semanticsTreeCreation.inMilliseconds});
......
...@@ -44,13 +44,16 @@ const int kExitCodeSuccess = 0; ...@@ -44,13 +44,16 @@ const int kExitCodeSuccess = 0;
final GetStackPointerCallback getStackPointer = () { final GetStackPointerCallback getStackPointer = () {
// Makes sure we are running on an Android arm64 device. // Makes sure we are running on an Android arm64 device.
if (!io.Platform.isAndroid) if (!io.Platform.isAndroid) {
throw 'This benchmark test can only be run on Android arm devices.'; throw 'This benchmark test can only be run on Android arm devices.';
}
final io.ProcessResult result = io.Process.runSync('getprop', <String>['ro.product.cpu.abi']); final io.ProcessResult result = io.Process.runSync('getprop', <String>['ro.product.cpu.abi']);
if (result.exitCode != 0) if (result.exitCode != 0) {
throw 'Failed to retrieve CPU information.'; throw 'Failed to retrieve CPU information.';
if (!result.stdout.toString().contains('armeabi')) }
if (!result.stdout.toString().contains('armeabi')) {
throw 'This benchmark test can only be run on Android arm devices.'; throw 'This benchmark test can only be run on Android arm devices.';
}
// Creates a block of memory to store the assembly code. // Creates a block of memory to store the assembly code.
final ffi.Pointer<ffi.Void> region = mmap(ffi.nullptr, kMemorySize, kProtRead | kProtWrite, final ffi.Pointer<ffi.Void> region = mmap(ffi.nullptr, kMemorySize, kProtRead | kProtWrite,
......
...@@ -159,8 +159,9 @@ class _Tester { ...@@ -159,8 +159,9 @@ class _Tester {
final Stopwatch stopwatch = Stopwatch()..start(); final Stopwatch stopwatch = Stopwatch()..start();
await gesture.moveTo(location, timeStamp: currentTime); await gesture.moveTo(location, timeStamp: currentTime);
stopwatch.stop(); stopwatch.stop();
if (onDataPoint != null) if (onDataPoint != null) {
onDataPoint(stopwatch.elapsed); onDataPoint(stopwatch.elapsed);
}
await _UntilNextFrame.wait(); await _UntilNextFrame.wait();
} }
......
...@@ -181,8 +181,9 @@ class _Tester { ...@@ -181,8 +181,9 @@ class _Tester {
final Stopwatch stopwatch = Stopwatch()..start(); final Stopwatch stopwatch = Stopwatch()..start();
await gesture.moveTo(location, timeStamp: currentTime); await gesture.moveTo(location, timeStamp: currentTime);
stopwatch.stop(); stopwatch.stop();
if (onDataPoint != null) if (onDataPoint != null) {
onDataPoint(stopwatch.elapsed); onDataPoint(stopwatch.elapsed);
}
await _UntilNextFrame.wait(); await _UntilNextFrame.wait();
} }
......
...@@ -405,8 +405,9 @@ abstract class WidgetRecorder extends Recorder implements FrameRecorder { ...@@ -405,8 +405,9 @@ abstract class WidgetRecorder extends Recorder implements FrameRecorder {
if (shouldContinue()) { if (shouldContinue()) {
PlatformDispatcher.instance.scheduleFrame(); PlatformDispatcher.instance.scheduleFrame();
} else { } else {
for (final VoidCallback fn in _didStopCallbacks) for (final VoidCallback fn in _didStopCallbacks) {
fn(); fn();
}
_runCompleter!.complete(); _runCompleter!.complete();
} }
} }
...@@ -520,8 +521,9 @@ abstract class WidgetBuildRecorder extends Recorder implements FrameRecorder { ...@@ -520,8 +521,9 @@ abstract class WidgetBuildRecorder extends Recorder implements FrameRecorder {
showWidget = !showWidget; showWidget = !showWidget;
_hostState._setStateTrampoline(); _hostState._setStateTrampoline();
} else { } else {
for (final VoidCallback fn in _didStopCallbacks) for (final VoidCallback fn in _didStopCallbacks) {
fn(); fn();
}
_runCompleter!.complete(); _runCompleter!.complete();
} }
} }
......
...@@ -39,13 +39,14 @@ void main() { ...@@ -39,13 +39,14 @@ void main() {
watch.stop(); watch.stop();
final int elapsed = watch.elapsedMicroseconds; final int elapsed = watch.elapsedMicroseconds;
final double averagePerIteration = elapsed / iteration; final double averagePerIteration = elapsed / iteration;
if (addResult) if (addResult) {
printer.addResult( printer.addResult(
description: '$name ($listenerCount listeners)', description: '$name ($listenerCount listeners)',
value: averagePerIteration * _kScale, value: averagePerIteration * _kScale,
unit: 'ns per iteration', unit: 'ns per iteration',
name: '$name$listenerCount', name: '$name$listenerCount',
); );
}
} }
} }
...@@ -65,13 +66,14 @@ void main() { ...@@ -65,13 +66,14 @@ void main() {
watch.stop(); watch.stop();
final int elapsed = watch.elapsedMicroseconds; final int elapsed = watch.elapsedMicroseconds;
final double averagePerIteration = elapsed / iteration; final double averagePerIteration = elapsed / iteration;
if (addResult) if (addResult) {
printer.addResult( printer.addResult(
description: '$name ($listenerCount listeners)', description: '$name ($listenerCount listeners)',
value: averagePerIteration * _kScale, value: averagePerIteration * _kScale,
unit: 'ns per iteration', unit: 'ns per iteration',
name: '$name$listenerCount', name: '$name$listenerCount',
); );
}
} }
} }
...@@ -107,13 +109,14 @@ void main() { ...@@ -107,13 +109,14 @@ void main() {
watch.stop(); watch.stop();
final int elapsed = watch.elapsedMicroseconds; final int elapsed = watch.elapsedMicroseconds;
final double averagePerIteration = elapsed / iteration; final double averagePerIteration = elapsed / iteration;
if (addResult) if (addResult) {
printer.addResult( printer.addResult(
description: '$name ($listenerCount listeners)', description: '$name ($listenerCount listeners)',
value: averagePerIteration * _kScale, value: averagePerIteration * _kScale,
unit: 'ns per iteration', unit: 'ns per iteration',
name: '$name$listenerCount', name: '$name$listenerCount',
); );
}
} }
} }
...@@ -156,13 +159,14 @@ void main() { ...@@ -156,13 +159,14 @@ void main() {
watch.stop(); watch.stop();
final int elapsed = watch.elapsedMicroseconds; final int elapsed = watch.elapsedMicroseconds;
final double averagePerIteration = elapsed / iteration; final double averagePerIteration = elapsed / iteration;
if (addResult) if (addResult) {
printer.addResult( printer.addResult(
description: '$name ($listenerCount listeners)', description: '$name ($listenerCount listeners)',
value: averagePerIteration * _kScale, value: averagePerIteration * _kScale,
unit: 'ns per iteration', unit: 'ns per iteration',
name: '$name$listenerCount', name: '$name$listenerCount',
); );
}
} }
} }
......
...@@ -32,10 +32,12 @@ void main() { ...@@ -32,10 +32,12 @@ void main() {
watch.start(); watch.start();
for (int i = 0; i < _kNumIters; i += 1) { for (int i = 0; i < _kNumIters; i += 1) {
for (final PointerEvent event in velocityEventData) { for (final PointerEvent event in velocityEventData) {
if (event is PointerDownEvent || event is PointerMoveEvent) if (event is PointerDownEvent || event is PointerMoveEvent) {
tracker.addPosition(event.timeStamp, event.position); tracker.addPosition(event.timeStamp, event.position);
if (event is PointerUpEvent) }
if (event is PointerUpEvent) {
tracker.getVelocity(); tracker.getVelocity();
}
} }
} }
watch.stop(); watch.stop();
......
...@@ -67,8 +67,9 @@ class StockArrow extends StatelessWidget { ...@@ -67,8 +67,9 @@ class StockArrow extends StatelessWidget {
} }
Color _colorForPercentChange(double percentChange) { Color _colorForPercentChange(double percentChange) {
if (percentChange > 0) if (percentChange > 0) {
return Colors.green[_colorIndexForPercentChange(percentChange)]!; return Colors.green[_colorIndexForPercentChange(percentChange)]!;
}
return Colors.red[_colorIndexForPercentChange(percentChange)]!; return Colors.red[_colorIndexForPercentChange(percentChange)]!;
} }
......
...@@ -85,8 +85,9 @@ class StockHomeState extends State<StockHome> { ...@@ -85,8 +85,9 @@ class StockHomeState extends State<StockHome> {
} }
void _handleStockModeChange(StockMode? value) { void _handleStockModeChange(StockMode? value) {
if (widget.updater != null) if (widget.updater != null) {
widget.updater(widget.configuration.copyWith(stockMode: value)); widget.updater(widget.configuration.copyWith(stockMode: value));
}
} }
void _handleStockMenu(BuildContext context, _StockMenuItem value) { void _handleStockMenu(BuildContext context, _StockMenuItem value) {
...@@ -239,8 +240,9 @@ class StockHomeState extends State<StockHome> { ...@@ -239,8 +240,9 @@ class StockHomeState extends State<StockHome> {
} }
Iterable<Stock> _filterBySearchQuery(Iterable<Stock> stocks) { Iterable<Stock> _filterBySearchQuery(Iterable<Stock> stocks) {
if (_searchQuery.text.isEmpty) if (_searchQuery.text.isEmpty) {
return stocks; return stocks;
}
final RegExp regexp = RegExp(_searchQuery.text, caseSensitive: false); final RegExp regexp = RegExp(_searchQuery.text, caseSensitive: false);
return stocks.where((Stock stock) => stock.symbol.contains(regexp)); return stocks.where((Stock stock) => stock.symbol.contains(regexp));
} }
......
...@@ -32,8 +32,9 @@ class StockRow extends StatelessWidget { ...@@ -32,8 +32,9 @@ class StockRow extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}'; final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}';
String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%'; String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%';
if (stock.percentChange > 0) if (stock.percentChange > 0) {
changeInPrice = '+$changeInPrice'; changeInPrice = '+$changeInPrice';
}
return InkWell( return InkWell(
key: ValueKey<String>(stock.symbol), key: ValueKey<String>(stock.symbol),
onTap: _getHandler(onPressed), onTap: _getHandler(onPressed),
......
...@@ -93,8 +93,9 @@ class StockSettingsState extends State<StockSettings> { ...@@ -93,8 +93,9 @@ class StockSettingsState extends State<StockSettings> {
} }
void sendUpdates(StockConfiguration value) { void sendUpdates(StockConfiguration value) {
if (widget.updater != null) if (widget.updater != null) {
widget.updater(value); widget.updater(value);
}
} }
AppBar buildAppBar(BuildContext context) { AppBar buildAppBar(BuildContext context) {
......
...@@ -21,8 +21,9 @@ class _StockSymbolView extends StatelessWidget { ...@@ -21,8 +21,9 @@ class _StockSymbolView extends StatelessWidget {
assert(stock != null); assert(stock != null);
final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}'; final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}';
String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%'; String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%';
if (stock.percentChange > 0) if (stock.percentChange > 0) {
changeInPrice = '+$changeInPrice'; changeInPrice = '+$changeInPrice';
}
final TextStyle headings = Theme.of(context).textTheme.bodyText1!; final TextStyle headings = Theme.of(context).textTheme.bodyText1!;
return Container( return Container(
......
...@@ -9,8 +9,9 @@ import 'package:stocks/stock_data.dart' as stock_data; ...@@ -9,8 +9,9 @@ import 'package:stocks/stock_data.dart' as stock_data;
Element? findElementOfExactWidgetTypeGoingDown(Element node, Type targetType) { Element? findElementOfExactWidgetTypeGoingDown(Element node, Type targetType) {
void walker(Element child) { void walker(Element child) {
if (child.widget.runtimeType == targetType) if (child.widget.runtimeType == targetType) {
throw child; throw child;
}
child.visitChildElements(walker); child.visitChildElements(walker);
} }
try { try {
......
This diff is collapsed.
...@@ -396,12 +396,14 @@ class _SnippetChecker { ...@@ -396,12 +396,14 @@ class _SnippetChecker {
} }
} }
} }
if (!silent) if (!silent) {
print('Found ${sections.length} snippet code blocks'); print('Found ${sections.length} snippet code blocks');
}
for (final _Section section in sections) { for (final _Section section in sections) {
final String path = _writeSection(section).path; final String path = _writeSection(section).path;
if (sectionMap != null) if (sectionMap != null) {
sectionMap[path] = section; sectionMap[path] = section;
}
} }
} }
...@@ -448,8 +450,9 @@ class _SnippetChecker { ...@@ -448,8 +450,9 @@ class _SnippetChecker {
/// Invokes the analyzer on the given [directory] and returns the stdout. /// Invokes the analyzer on the given [directory] and returns the stdout.
int _runAnalyzer(Directory directory, {bool silent = true, required List<String> output}) { int _runAnalyzer(Directory directory, {bool silent = true, required List<String> output}) {
if (!silent) if (!silent) {
print('Starting analysis of code snippets.'); print('Starting analysis of code snippets.');
}
_createConfigurationFiles(directory); _createConfigurationFiles(directory);
final ProcessResult result = Process.runSync( final ProcessResult result = Process.runSync(
_flutter, _flutter,
...@@ -598,8 +601,9 @@ class _SnippetChecker { ...@@ -598,8 +601,9 @@ class _SnippetChecker {
exitCode = 0; exitCode = 0;
} }
if (exitCode == 0) { if (exitCode == 0) {
if (!silent) if (!silent) {
print('No analysis errors in snippets!'); print('No analysis errors in snippets!');
}
assert(analysisErrors.isEmpty); assert(analysisErrors.isEmpty);
} }
return _AnalysisResult(exitCode, analysisErrors); return _AnalysisResult(exitCode, analysisErrors);
...@@ -633,17 +637,19 @@ class _SnippetChecker { ...@@ -633,17 +637,19 @@ class _SnippetChecker {
// Each section of the dart code that is either split by a blank line, or with '// ...' is // Each section of the dart code that is either split by a blank line, or with '// ...' is
// treated as a separate code block. // treated as a separate code block.
if (block[index] == '' || block[index] == '// ...') { if (block[index] == '' || block[index] == '// ...') {
if (subline == null) if (subline == null) {
throw _SnippetCheckerException('${_Line(filename: line.filename, line: line.line + index, indent: line.indent)}: ' throw _SnippetCheckerException('${_Line(filename: line.filename, line: line.line + index, indent: line.indent)}: '
'Unexpected blank line or "// ..." line near start of subblock in snippet code.'); 'Unexpected blank line or "// ..." line near start of subblock in snippet code.');
}
subblocks += 1; subblocks += 1;
subsections.add(_processBlock(subline, buffer)); subsections.add(_processBlock(subline, buffer));
buffer.clear(); buffer.clear();
assert(buffer.isEmpty); assert(buffer.isEmpty);
subline = null; subline = null;
} else if (block[index].startsWith('// ')) { } else if (block[index].startsWith('// ')) {
if (buffer.length > 1) // don't include leading comments if (buffer.length > 1) {
buffer.add('/${block[index]}'); // so that it doesn't start with "// " and get caught in this again buffer.add('/${block[index]}'); // so that it doesn't start with "// " and get caught in this again
}
} else { } else {
subline ??= _Line( subline ??= _Line(
code: block[index], code: block[index],
......
...@@ -133,10 +133,12 @@ class FlutterCompactFormatter { ...@@ -133,10 +133,12 @@ class FlutterCompactFormatter {
originalResult.errorMessage = error; originalResult.errorMessage = error;
originalResult.stackTrace = stackTrace; originalResult.stackTrace = stackTrace;
} else { } else {
if (error != null) if (error != null) {
stderr.writeln(error); stderr.writeln(error);
if (stackTrace != null) }
if (stackTrace != null) {
stderr.writeln(stackTrace); stderr.writeln(stackTrace);
}
} }
break; break;
case 'print': case 'print':
......
...@@ -39,12 +39,15 @@ final String engineVersionFile = path.join(flutterRoot, 'bin', 'internal', 'engi ...@@ -39,12 +39,15 @@ final String engineVersionFile = path.join(flutterRoot, 'bin', 'internal', 'engi
final String flutterPluginsVersionFile = path.join(flutterRoot, 'bin', 'internal', 'flutter_plugins.version'); final String flutterPluginsVersionFile = path.join(flutterRoot, 'bin', 'internal', 'flutter_plugins.version');
String get platformFolderName { String get platformFolderName {
if (Platform.isWindows) if (Platform.isWindows) {
return 'windows-x64'; return 'windows-x64';
if (Platform.isMacOS) }
if (Platform.isMacOS) {
return 'darwin-x64'; return 'darwin-x64';
if (Platform.isLinux) }
if (Platform.isLinux) {
return 'linux-x64'; return 'linux-x64';
}
throw UnsupportedError('The platform ${Platform.operatingSystem} is not supported by this script.'); throw UnsupportedError('The platform ${Platform.operatingSystem} is not supported by this script.');
} }
final String flutterTester = path.join(flutterRoot, 'bin', 'cache', 'artifacts', 'engine', platformFolderName, 'flutter_tester$exe'); final String flutterTester = path.join(flutterRoot, 'bin', 'cache', 'artifacts', 'engine', platformFolderName, 'flutter_tester$exe');
...@@ -187,8 +190,9 @@ Future<void> main(List<String> args) async { ...@@ -187,8 +190,9 @@ Future<void> main(List<String> args) async {
} }
} }
flutterTestArgs.removeWhere((String arg) => removeArgs.contains(arg)); flutterTestArgs.removeWhere((String arg) => removeArgs.contains(arg));
if (Platform.environment.containsKey(CIRRUS_TASK_NAME)) if (Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
print('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}'); print('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}');
}
print('═' * 80); print('═' * 80);
await selectShard(<String, ShardRunner>{ await selectShard(<String, ShardRunner>{
'add_to_app_life_cycle_tests': _runAddToAppLifeCycleTests, 'add_to_app_life_cycle_tests': _runAddToAppLifeCycleTests,
...@@ -327,8 +331,9 @@ Future<void> _runTestHarnessTests() async { ...@@ -327,8 +331,9 @@ Future<void> _runTestHarnessTests() async {
// Verify that we correctly generated the version file. // Verify that we correctly generated the version file.
final String? versionError = await verifyVersion(File(path.join(flutterRoot, 'version'))); final String? versionError = await verifyVersion(File(path.join(flutterRoot, 'version')));
if (versionError != null) if (versionError != null) {
exitWithError(<String>[versionError]); exitWithError(<String>[versionError]);
}
} }
final String _toolsPath = path.join(flutterRoot, 'packages', 'flutter_tools'); final String _toolsPath = path.join(flutterRoot, 'packages', 'flutter_tools');
...@@ -1748,8 +1753,9 @@ Future<void> _runFlutterTest(String workingDirectory, { ...@@ -1748,8 +1753,9 @@ Future<void> _runFlutterTest(String workingDirectory, {
]; ];
final bool shouldProcessOutput = useFlutterTestFormatter && !expectFailure && !options.contains('--coverage'); final bool shouldProcessOutput = useFlutterTestFormatter && !expectFailure && !options.contains('--coverage');
if (shouldProcessOutput) if (shouldProcessOutput) {
args.add('--machine'); args.add('--machine');
}
if (script != null) { if (script != null) {
final String fullScriptPath = path.join(workingDirectory, script); final String fullScriptPath = path.join(workingDirectory, script);
...@@ -1757,8 +1763,9 @@ Future<void> _runFlutterTest(String workingDirectory, { ...@@ -1757,8 +1763,9 @@ Future<void> _runFlutterTest(String workingDirectory, {
print('${red}Could not find test$reset: $green$fullScriptPath$reset'); print('${red}Could not find test$reset: $green$fullScriptPath$reset');
print('Working directory: $cyan$workingDirectory$reset'); print('Working directory: $cyan$workingDirectory$reset');
print('Script: $green$script$reset'); print('Script: $green$script$reset');
if (!printOutput) if (!printOutput) {
print('This is one of the tests that does not normally print output.'); print('This is one of the tests that does not normally print output.');
}
exit(1); exit(1);
} }
args.add(script); args.add(script);
...@@ -1782,8 +1789,9 @@ Future<void> _runFlutterTest(String workingDirectory, { ...@@ -1782,8 +1789,9 @@ Future<void> _runFlutterTest(String workingDirectory, {
if (outputChecker != null) { if (outputChecker != null) {
final String? message = outputChecker(result); final String? message = outputChecker(result);
if (message != null) if (message != null) {
exitWithError(<String>[message]); exitWithError(<String>[message]);
}
} }
return; return;
} }
...@@ -1862,12 +1870,15 @@ Future<String?> verifyVersion(File file) async { ...@@ -1862,12 +1870,15 @@ Future<String?> verifyVersion(File file) async {
final RegExp pattern = RegExp( final RegExp pattern = RegExp(
r'^(\d+)\.(\d+)\.(\d+)((-\d+\.\d+)?\.pre(\.\d+)?)?$'); r'^(\d+)\.(\d+)\.(\d+)((-\d+\.\d+)?\.pre(\.\d+)?)?$');
final String version = await file.readAsString(); final String version = await file.readAsString();
if (!file.existsSync()) if (!file.existsSync()) {
return 'The version logic failed to create the Flutter version file.'; return 'The version logic failed to create the Flutter version file.';
if (version == '0.0.0-unknown') }
if (version == '0.0.0-unknown') {
return 'The version logic failed to determine the Flutter version.'; return 'The version logic failed to determine the Flutter version.';
if (!version.contains(pattern)) }
if (!version.contains(pattern)) {
return 'The version logic generated an invalid version string: "$version".'; return 'The version logic generated an invalid version string: "$version".';
}
return null; return null;
} }
......
...@@ -59,8 +59,9 @@ String get clock { ...@@ -59,8 +59,9 @@ String get clock {
String prettyPrintDuration(Duration duration) { String prettyPrintDuration(Duration duration) {
String result = ''; String result = '';
final int minutes = duration.inMinutes; final int minutes = duration.inMinutes;
if (minutes > 0) if (minutes > 0) {
result += '${minutes}min '; result += '${minutes}min ';
}
final int seconds = duration.inSeconds - minutes * 60; final int seconds = duration.inSeconds - minutes * 60;
final int milliseconds = duration.inMilliseconds - (seconds * 1000 + minutes * 60 * 1000); final int milliseconds = duration.inMilliseconds - (seconds * 1000 + minutes * 60 * 1000);
result += '$seconds.${milliseconds.toString().padLeft(3, "0")}s'; result += '$seconds.${milliseconds.toString().padLeft(3, "0")}s';
......
...@@ -8,6 +8,5 @@ analyzer: ...@@ -8,6 +8,5 @@ analyzer:
linter: linter:
rules: rules:
avoid_catches_without_on_clauses: true avoid_catches_without_on_clauses: true
curly_braces_in_flow_control_structures: true
prefer_relative_imports: true prefer_relative_imports: true
unawaited_futures: true unawaited_futures: true
...@@ -31,42 +31,54 @@ class CustomerTest { ...@@ -31,42 +31,54 @@ class CustomerTest {
test.add(line.substring(5)); test.add(line.substring(5));
} else if (line.startsWith('test.windows=')) { } else if (line.startsWith('test.windows=')) {
hasTests = true; hasTests = true;
if (Platform.isWindows) if (Platform.isWindows) {
test.add(line.substring(13)); test.add(line.substring(13));
}
} else if (line.startsWith('test.macos=')) { } else if (line.startsWith('test.macos=')) {
hasTests = true; hasTests = true;
if (Platform.isMacOS) if (Platform.isMacOS) {
test.add(line.substring(11)); test.add(line.substring(11));
}
} else if (line.startsWith('test.linux=')) { } else if (line.startsWith('test.linux=')) {
hasTests = true; hasTests = true;
if (Platform.isLinux) if (Platform.isLinux) {
test.add(line.substring(11)); test.add(line.substring(11));
}
} else if (line.startsWith('test.posix=')) { } else if (line.startsWith('test.posix=')) {
hasTests = true; hasTests = true;
if (Platform.isLinux || Platform.isMacOS) if (Platform.isLinux || Platform.isMacOS) {
test.add(line.substring(11)); test.add(line.substring(11));
}
} else { } else {
throw FormatException('${errorPrefix}Unexpected directive:\n$line'); throw FormatException('${errorPrefix}Unexpected directive:\n$line');
} }
} }
if (contacts.isEmpty) if (contacts.isEmpty) {
throw FormatException('${errorPrefix}No contacts specified. At least one contact e-mail address must be specified.'); throw FormatException('${errorPrefix}No contacts specified. At least one contact e-mail address must be specified.');
}
for (final String email in contacts) { for (final String email in contacts) {
if (!email.contains(_email) || email.endsWith('@example.com')) if (!email.contains(_email) || email.endsWith('@example.com')) {
throw FormatException('${errorPrefix}The following e-mail address appears to be an invalid e-mail address: $email'); throw FormatException('${errorPrefix}The following e-mail address appears to be an invalid e-mail address: $email');
}
} }
if (fetch.isEmpty) if (fetch.isEmpty) {
throw FormatException('${errorPrefix}No "fetch" directives specified. Two lines are expected: "git clone https://github.com/USERNAME/REPOSITORY.git tests" and "git -C tests checkout HASH".'); throw FormatException('${errorPrefix}No "fetch" directives specified. Two lines are expected: "git clone https://github.com/USERNAME/REPOSITORY.git tests" and "git -C tests checkout HASH".');
if (fetch.length < 2) }
if (fetch.length < 2) {
throw FormatException('${errorPrefix}Only one "fetch" directive specified. Two lines are expected: "git clone https://github.com/USERNAME/REPOSITORY.git tests" and "git -C tests checkout HASH".'); throw FormatException('${errorPrefix}Only one "fetch" directive specified. Two lines are expected: "git clone https://github.com/USERNAME/REPOSITORY.git tests" and "git -C tests checkout HASH".');
if (!fetch[0].contains(_fetch1)) }
if (!fetch[0].contains(_fetch1)) {
throw FormatException('${errorPrefix}First "fetch" directive does not match expected pattern (expected "git clone https://github.com/USERNAME/REPOSITORY.git tests").'); throw FormatException('${errorPrefix}First "fetch" directive does not match expected pattern (expected "git clone https://github.com/USERNAME/REPOSITORY.git tests").');
if (!fetch[1].contains(_fetch2)) }
if (!fetch[1].contains(_fetch2)) {
throw FormatException('${errorPrefix}Second "fetch" directive does not match expected pattern (expected "git -C tests checkout HASH").'); throw FormatException('${errorPrefix}Second "fetch" directive does not match expected pattern (expected "git -C tests checkout HASH").');
if (update.isEmpty) }
if (update.isEmpty) {
throw FormatException('${errorPrefix}No "update" directives specified. At least one directory must be specified. (It can be "." to just upgrade the root of the repository.)'); throw FormatException('${errorPrefix}No "update" directives specified. At least one directory must be specified. (It can be "." to just upgrade the root of the repository.)');
if (!hasTests) }
if (!hasTests) {
throw FormatException('${errorPrefix}No "test" directives specified. At least one command must be specified to run tests.'); throw FormatException('${errorPrefix}No "test" directives specified. At least one command must be specified to run tests.');
}
return CustomerTest._( return CustomerTest._(
List<String>.unmodifiable(contacts), List<String>.unmodifiable(contacts),
List<String>.unmodifiable(fetch), List<String>.unmodifiable(fetch),
......
...@@ -17,8 +17,9 @@ Future<bool> runTests({ ...@@ -17,8 +17,9 @@ Future<bool> runTests({
int shardIndex = 0, int shardIndex = 0,
required List<File> files, required List<File> files,
}) async { }) async {
if (verbose) if (verbose) {
print('Starting run_tests.dart...'); print('Starting run_tests.dart...');
}
// Best attempt at evenly splitting tests among the shards // Best attempt at evenly splitting tests among the shards
final List<File> shardedFiles = <File>[]; final List<File> shardedFiles = <File>[];
...@@ -46,18 +47,21 @@ Future<bool> runTests({ ...@@ -46,18 +47,21 @@ Future<bool> runTests({
} else { } else {
print('Tests:'); print('Tests:');
} }
for (final File file in shardedFiles) for (final File file in shardedFiles) {
print(file.path); print(file.path);
}
} }
print(''); print('');
for (final File file in shardedFiles) { for (final File file in shardedFiles) {
if (verbose) if (verbose) {
print('Processing ${file.path}...'); print('Processing ${file.path}...');
}
void printHeader() { void printHeader() {
if (!verbose) if (!verbose) {
print('Processing ${file.path}...'); print('Processing ${file.path}...');
}
} }
void failure(String message) { void failure(String message) {
...@@ -82,8 +86,9 @@ Future<bool> runTests({ ...@@ -82,8 +86,9 @@ Future<bool> runTests({
bool success = true; bool success = true;
final Directory checkout = Directory.systemTemp.createTempSync('flutter_customer_testing.${path.basenameWithoutExtension(file.path)}.'); final Directory checkout = Directory.systemTemp.createTempSync('flutter_customer_testing.${path.basenameWithoutExtension(file.path)}.');
if (verbose) if (verbose) {
print('Created temporary directory: ${checkout.path}'); print('Created temporary directory: ${checkout.path}');
}
try { try {
assert(instructions.fetch.isNotEmpty); assert(instructions.fetch.isNotEmpty);
for (final String fetchCommand in instructions.fetch) { for (final String fetchCommand in instructions.fetch) {
...@@ -105,8 +110,9 @@ Future<bool> runTests({ ...@@ -105,8 +110,9 @@ Future<bool> runTests({
final Directory customerRepo = Directory(path.join(checkout.path, 'tests')); final Directory customerRepo = Directory(path.join(checkout.path, 'tests'));
for (final Directory updateDirectory in instructions.update) { for (final Directory updateDirectory in instructions.update) {
final Directory resolvedUpdateDirectory = Directory(path.join(customerRepo.path, updateDirectory.path)); final Directory resolvedUpdateDirectory = Directory(path.join(customerRepo.path, updateDirectory.path));
if (verbose) if (verbose) {
print('Updating code in ${resolvedUpdateDirectory.path}...'); print('Updating code in ${resolvedUpdateDirectory.path}...');
}
if (!File(path.join(resolvedUpdateDirectory.path, 'pubspec.yaml')).existsSync()) { if (!File(path.join(resolvedUpdateDirectory.path, 'pubspec.yaml')).existsSync()) {
failure('The directory ${updateDirectory.path}, which was specified as an update directory, does not contain a "pubspec.yaml" file.'); failure('The directory ${updateDirectory.path}, which was specified as an update directory, does not contain a "pubspec.yaml" file.');
success = false; success = false;
...@@ -124,11 +130,13 @@ Future<bool> runTests({ ...@@ -124,11 +130,13 @@ Future<bool> runTests({
} }
} }
if (success) { if (success) {
if (verbose) if (verbose) {
print('Running tests...'); print('Running tests...');
}
for (int iteration = 0; iteration < repeat; iteration += 1) { for (int iteration = 0; iteration < repeat; iteration += 1) {
if (verbose && repeat > 1) if (verbose && repeat > 1) {
print('Round ${iteration + 1} of $repeat.'); print('Round ${iteration + 1} of $repeat.');
}
for (final String testCommand in instructions.tests) { for (final String testCommand in instructions.tests) {
testCount += 1; testCount += 1;
success = await shell(testCommand, customerRepo, verbose: verbose, failedCallback: printHeader); success = await shell(testCommand, customerRepo, verbose: verbose, failedCallback: printHeader);
...@@ -138,13 +146,15 @@ Future<bool> runTests({ ...@@ -138,13 +146,15 @@ Future<bool> runTests({
} }
} }
} }
if (verbose && success) if (verbose && success) {
print('Tests finished.'); print('Tests finished.');
}
} }
} }
} finally { } finally {
if (verbose) if (verbose) {
print('Deleting temporary directory...'); print('Deleting temporary directory...');
}
try { try {
checkout.deleteSync(recursive: true); checkout.deleteSync(recursive: true);
} on FileSystemException { } on FileSystemException {
...@@ -155,8 +165,9 @@ Future<bool> runTests({ ...@@ -155,8 +165,9 @@ Future<bool> runTests({
final String s = instructions.contacts.length == 1 ? '' : 's'; final String s = instructions.contacts.length == 1 ? '' : 's';
print('Contact$s: ${instructions.contacts.join(", ")}'); print('Contact$s: ${instructions.contacts.join(", ")}');
} }
if (verbose || !success) if (verbose || !success) {
print(''); print('');
}
} }
if (failures > 0) { if (failures > 0) {
final String s = failures == 1 ? '' : 's'; final String s = failures == 1 ? '' : 's';
...@@ -170,8 +181,9 @@ Future<bool> runTests({ ...@@ -170,8 +181,9 @@ Future<bool> runTests({
final RegExp _spaces = RegExp(r' +'); final RegExp _spaces = RegExp(r' +');
Future<bool> shell(String command, Directory directory, { bool verbose = false, bool silentFailure = false, void Function()? failedCallback }) async { Future<bool> shell(String command, Directory directory, { bool verbose = false, bool silentFailure = false, void Function()? failedCallback }) async {
if (verbose) if (verbose) {
print('>> $command'); print('>> $command');
}
Process process; Process process;
if (Platform.isWindows) { if (Platform.isWindows) {
process = await Process.start('CMD.EXE', <String>['/S', '/C', command], workingDirectory: directory.path); process = await Process.start('CMD.EXE', <String>['/S', '/C', command], workingDirectory: directory.path);
...@@ -183,11 +195,13 @@ Future<bool> shell(String command, Directory directory, { bool verbose = false, ...@@ -183,11 +195,13 @@ Future<bool> shell(String command, Directory directory, { bool verbose = false,
utf8.decoder.bind(process.stdout).transform(const LineSplitter()).listen(verbose ? printLog : output.add); utf8.decoder.bind(process.stdout).transform(const LineSplitter()).listen(verbose ? printLog : output.add);
utf8.decoder.bind(process.stderr).transform(const LineSplitter()).listen(verbose ? printLog : output.add); utf8.decoder.bind(process.stderr).transform(const LineSplitter()).listen(verbose ? printLog : output.add);
final bool success = await process.exitCode == 0; final bool success = await process.exitCode == 0;
if (success || silentFailure) if (success || silentFailure) {
return success; return success;
}
if (!verbose) { if (!verbose) {
if (failedCallback != null) if (failedCallback != null) {
failedCallback(); failedCallback();
}
print('>> $command'); print('>> $command');
output.forEach(printLog); output.forEach(printLog);
} }
......
...@@ -92,8 +92,9 @@ Future<bool> run(List<String> arguments) async { ...@@ -92,8 +92,9 @@ Future<bool> run(List<String> arguments) async {
if (help || repeat == null || files.isEmpty || numberShards == null || numberShards <= 0 || shardIndex == null || shardIndex < 0) { if (help || repeat == null || files.isEmpty || numberShards == null || numberShards <= 0 || shardIndex == null || shardIndex < 0) {
printHelp(); printHelp();
if (verbose) { if (verbose) {
if (repeat == null) if (repeat == null) {
print('Error: Could not parse repeat count ("${parsedArguments['repeat']}")'); print('Error: Could not parse repeat count ("${parsedArguments['repeat']}")');
}
if (numberShards == null) { if (numberShards == null) {
print('Error: Could not parse shards count ("${parsedArguments['shards']}")'); print('Error: Could not parse shards count ("${parsedArguments['shards']}")');
} else if (numberShards < 1) { } else if (numberShards < 1) {
...@@ -121,8 +122,9 @@ Future<bool> run(List<String> arguments) async { ...@@ -121,8 +122,9 @@ Future<bool> run(List<String> arguments) async {
return false; return false;
} }
if (files.length < numberShards) if (files.length < numberShards) {
print('Warning: There are more shards than tests. Some shards will not run any tests.'); print('Warning: There are more shards than tests. Some shards will not run any tests.');
}
return runTests( return runTests(
repeat: repeat, repeat: repeat,
......
...@@ -19,8 +19,9 @@ Future<void> main() async { ...@@ -19,8 +19,9 @@ Future<void> main() async {
section('Find Java'); section('Find Java');
final String? javaHome = await findJavaHome(); final String? javaHome = await findJavaHome();
if (javaHome == null) if (javaHome == null) {
return TaskResult.failure('Could not find Java'); return TaskResult.failure('Could not find Java');
}
print('\nUsing JAVA_HOME=$javaHome'); print('\nUsing JAVA_HOME=$javaHome');
section('Create project'); section('Create project');
......
...@@ -19,8 +19,9 @@ Future<void> main() async { ...@@ -19,8 +19,9 @@ Future<void> main() async {
section('Find Java'); section('Find Java');
final String? javaHome = await findJavaHome(); final String? javaHome = await findJavaHome();
if (javaHome == null) if (javaHome == null) {
return TaskResult.failure('Could not find Java'); return TaskResult.failure('Could not find Java');
}
print('\nUsing JAVA_HOME=$javaHome'); print('\nUsing JAVA_HOME=$javaHome');
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.'); final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
......
...@@ -42,8 +42,9 @@ class BackButtonMemoryTest extends MemoryTest { ...@@ -42,8 +42,9 @@ class BackButtonMemoryTest extends MemoryTest {
prepareForNextMessage('READY'); prepareForNextMessage('READY');
final String output = await device!.shellEval('am', <String>['start', '-n', '$packageName/$activityName']); final String output = await device!.shellEval('am', <String>['start', '-n', '$packageName/$activityName']);
print('adb shell am start: $output'); print('adb shell am start: $output');
if (output.contains('Error')) if (output.contains('Error')) {
fail('unable to launch activity'); fail('unable to launch activity');
}
await receivedNextMessage; await receivedNextMessage;
// Wait for the Flutter app to settle (e.g. run GCs). // Wait for the Flutter app to settle (e.g. run GCs).
......
...@@ -90,12 +90,15 @@ Future<int> runTest({bool coverage = false, bool noPub = false}) async { ...@@ -90,12 +90,15 @@ Future<int> runTest({bool coverage = false, bool noPub = false}) async {
}); });
final int result = await analysis.exitCode; final int result = await analysis.exitCode;
clock.stop(); clock.stop();
if (result != 0) if (result != 0) {
throw Exception('flutter test failed with exit code $result'); throw Exception('flutter test failed with exit code $result');
if (badLines > 0) }
if (badLines > 0) {
throw Exception('flutter test rendered unexpected output ($badLines bad lines)'); throw Exception('flutter test rendered unexpected output ($badLines bad lines)');
if (step != TestStep.testPassed) }
if (step != TestStep.testPassed) {
throw Exception('flutter test did not finish (only reached step $step)'); throw Exception('flutter test did not finish (only reached step $step)');
}
print('elapsed time: ${clock.elapsedMilliseconds}ms'); print('elapsed time: ${clock.elapsedMilliseconds}ms');
return clock.elapsedMilliseconds; return clock.elapsedMilliseconds;
} }
......
...@@ -258,19 +258,22 @@ Future<void> main() async { ...@@ -258,19 +258,22 @@ Future<void> main() async {
]); ]);
}); });
if (result.exitCode == 0) if (result.exitCode == 0) {
throw failure( throw failure(
'Gradle did not exit with error as expected', result); 'Gradle did not exit with error as expected', result);
}
String output = '${result.stdout}\n${result.stderr}'; String output = '${result.stdout}\n${result.stderr}';
if (output.contains('GradleException') || if (output.contains('GradleException') ||
output.contains('Failed to notify') || output.contains('Failed to notify') ||
output.contains('at org.gradle')) output.contains('at org.gradle')) {
throw failure( throw failure(
'Gradle output should not contain stacktrace', result); 'Gradle output should not contain stacktrace', result);
if (!output.contains('Build failed')) }
if (!output.contains('Build failed')) {
throw failure( throw failure(
'Gradle output should contain a readable error message', 'Gradle output should contain a readable error message',
result); result);
}
section('flutter build apk on build script with error'); section('flutter build apk on build script with error');
await project.introduceError(); await project.introduceError();
...@@ -280,18 +283,21 @@ Future<void> main() async { ...@@ -280,18 +283,21 @@ Future<void> main() async {
'--release', '--release',
]); ]);
}); });
if (result.exitCode == 0) if (result.exitCode == 0) {
throw failure( throw failure(
'flutter build apk should fail when Gradle does', result); 'flutter build apk should fail when Gradle does', result);
}
output = '${result.stdout}\n${result.stderr}'; output = '${result.stdout}\n${result.stderr}';
if (!output.contains('Build failed')) if (!output.contains('Build failed')) {
throw failure( throw failure(
'flutter build apk output should contain a readable Gradle error message', 'flutter build apk output should contain a readable Gradle error message',
result); result);
if (hasMultipleOccurrences(output, 'Build failed')) }
if (hasMultipleOccurrences(output, 'Build failed')) {
throw failure( throw failure(
'flutter build apk should not invoke Gradle repeatedly on error', 'flutter build apk should not invoke Gradle repeatedly on error',
result); result);
}
}); });
await runProjectTest((FlutterProject project) async { await runProjectTest((FlutterProject project) async {
...@@ -303,12 +309,14 @@ Future<void> main() async { ...@@ -303,12 +309,14 @@ Future<void> main() async {
'--release', '--release',
]); ]);
}); });
if (result.exitCode == 0) if (result.exitCode == 0) {
throw failure( throw failure(
'Gradle did not exit with error as expected', result); 'Gradle did not exit with error as expected', result);
}
final String output = '${result.stdout}\n${result.stderr}'; final String output = '${result.stdout}\n${result.stderr}';
if (!output.contains('No file or variants found for asset: lib/gallery/example_code.dart.')) if (!output.contains('No file or variants found for asset: lib/gallery/example_code.dart.')) {
throw failure(output, result); throw failure(output, result);
}
}); });
return TaskResult.success(null); return TaskResult.success(null);
......
...@@ -23,8 +23,9 @@ Future<void> main() async { ...@@ -23,8 +23,9 @@ Future<void> main() async {
section('Find Java'); section('Find Java');
final String? javaHome = await findJavaHome(); final String? javaHome = await findJavaHome();
if (javaHome == null) if (javaHome == null) {
return TaskResult.failure('Could not find Java'); return TaskResult.failure('Could not find Java');
}
print('\nUsing JAVA_HOME=$javaHome'); print('\nUsing JAVA_HOME=$javaHome');
section('Create Flutter module project'); section('Create Flutter module project');
......
...@@ -26,8 +26,9 @@ Future<void> main() async { ...@@ -26,8 +26,9 @@ Future<void> main() async {
section('Find Java'); section('Find Java');
final String? javaHome = await findJavaHome(); final String? javaHome = await findJavaHome();
if (javaHome == null) if (javaHome == null) {
return TaskResult.failure('Could not find Java'); return TaskResult.failure('Could not find Java');
}
print('\nUsing JAVA_HOME=$javaHome'); print('\nUsing JAVA_HOME=$javaHome');
section('Create Flutter module project'); section('Create Flutter module project');
......
...@@ -66,8 +66,9 @@ void main() { ...@@ -66,8 +66,9 @@ void main() {
}); });
unawaited(run.exitCode.then<void>((int exitCode) { ok = false; })); unawaited(run.exitCode.then<void>((int exitCode) { ok = false; }));
await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]); await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]);
if (!ok) if (!ok) {
throw 'Failed to run test app.'; throw 'Failed to run test app.';
}
print('drive: starting...'); print('drive: starting...');
final Process drive = await startProcess( final Process drive = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
...@@ -90,11 +91,13 @@ void main() { ...@@ -90,11 +91,13 @@ void main() {
await flutter('install', options: <String>[ await flutter('install', options: <String>[
'--uninstall-only', '--uninstall-only',
]); ]);
if (result != 0) if (result != 0) {
throw 'Failed to drive test app (exit code $result).'; throw 'Failed to drive test app (exit code $result).';
}
result = await run.exitCode; result = await run.exitCode;
if (result != 0) if (result != 0) {
throw 'Received unexpected exit code $result from run process.'; throw 'Received unexpected exit code $result from run process.';
}
}); });
return TaskResult.success(null); return TaskResult.success(null);
}); });
......
...@@ -52,8 +52,9 @@ void main() { ...@@ -52,8 +52,9 @@ void main() {
}); });
unawaited(run.exitCode.then<void>((int exitCode) { ok = false; })); unawaited(run.exitCode.then<void>((int exitCode) { ok = false; }));
await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]); await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]);
if (!ok) if (!ok) {
throw 'Failed to run test app.'; throw 'Failed to run test app.';
}
final VmService client = await vmServiceConnectUri('ws://localhost:$vmServicePort/ws'); final VmService client = await vmServiceConnectUri('ws://localhost:$vmServicePort/ws');
final VM vm = await client.getVM(); final VM vm = await client.getVM();
...@@ -114,14 +115,16 @@ void main() { ...@@ -114,14 +115,16 @@ void main() {
run.stdin.write('q'); run.stdin.write('q');
final int result = await run.exitCode; final int result = await run.exitCode;
if (result != 0) if (result != 0) {
throw 'Received unexpected exit code $result from run process.'; throw 'Received unexpected exit code $result from run process.';
}
}); });
return TaskResult.success(null); return TaskResult.success(null);
}); });
} }
void expect(bool value) { void expect(bool value) {
if (!value) if (!value) {
throw 'failed assertion in service extensions test'; throw 'failed assertion in service extensions test';
}
} }
...@@ -11,7 +11,7 @@ Future<void> main() async { ...@@ -11,7 +11,7 @@ Future<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.fake; deviceOperatingSystem = DeviceOperatingSystem.fake;
await task(() async { await task(() async {
final Device device = await devices.workingDevice; final Device device = await devices.workingDevice;
if (device.deviceId == 'FAKE_SUCCESS') if (device.deviceId == 'FAKE_SUCCESS') {
return TaskResult.success(<String, dynamic>{ return TaskResult.success(<String, dynamic>{
'metric1': 42, 'metric1': 42,
'metric2': 123, 'metric2': 123,
...@@ -20,7 +20,8 @@ Future<void> main() async { ...@@ -20,7 +20,8 @@ Future<void> main() async {
'metric1', 'metric1',
'metric2', 'metric2',
]); ]);
else } else {
return TaskResult.failure('Failed'); return TaskResult.failure('Failed');
}
}); });
} }
...@@ -36,35 +36,46 @@ final RegExp dartVersionPattern = RegExp(r'// *@dart *= *(\d+).(\d+)'); ...@@ -36,35 +36,46 @@ final RegExp dartVersionPattern = RegExp(r'// *@dart *= *(\d+).(\d+)');
final Version firstNullSafeDartVersion = Version(2, 12, 0); final Version firstNullSafeDartVersion = Version(2, 12, 0);
Future<double> findCostsForFile(File file) async { Future<double> findCostsForFile(File file) async {
if (path.extension(file.path) == '.py') if (path.extension(file.path) == '.py') {
return pythonCost; return pythonCost;
}
if (path.extension(file.path) != '.dart' && if (path.extension(file.path) != '.dart' &&
path.extension(file.path) != '.yaml' && path.extension(file.path) != '.yaml' &&
path.extension(file.path) != '.sh') path.extension(file.path) != '.sh') {
return 0.0; return 0.0;
}
final bool isTest = file.path.endsWith('_test.dart'); final bool isTest = file.path.endsWith('_test.dart');
final bool isDart = file.path.endsWith('.dart'); final bool isDart = file.path.endsWith('.dart');
double total = 0.0; double total = 0.0;
for (final String line in await file.readAsLines()) { for (final String line in await file.readAsLines()) {
if (line.contains(todoPattern)) if (line.contains(todoPattern)) {
total += todoCost; total += todoCost;
if (line.contains(ignorePattern)) }
if (line.contains(ignorePattern)) {
total += ignoreCost; total += ignoreCost;
if (line.contains(ignoreForFilePattern)) }
if (line.contains(ignoreForFilePattern)) {
total += ignoreForFileCost; total += ignoreForFileCost;
if (!isTest && line.contains(asDynamicPattern)) }
if (!isTest && line.contains(asDynamicPattern)) {
total += asDynamicCost; total += asDynamicCost;
if (line.contains(deprecationPattern)) }
if (line.contains(deprecationPattern)) {
total += deprecationCost; total += deprecationCost;
if (line.contains(legacyDeprecationPattern)) }
if (line.contains(legacyDeprecationPattern)) {
total += legacyDeprecationCost; total += legacyDeprecationCost;
if (isTest && line.contains('skip:') && !line.contains('[intended]')) }
if (isTest && line.contains('skip:') && !line.contains('[intended]')) {
total += skipCost; total += skipCost;
if (isDart && isOptingOutOfNullSafety(line)) }
if (isDart && isOptingOutOfNullSafety(line)) {
total += fileNullSafetyMigrationCost; total += fileNullSafetyMigrationCost;
}
} }
if (path.basename(file.path) == 'pubspec.yaml' && !packageIsNullSafe(file)) if (path.basename(file.path) == 'pubspec.yaml' && !packageIsNullSafe(file)) {
total += packageNullSafetyMigrationCost; total += packageNullSafetyMigrationCost;
}
return total; return total;
} }
...@@ -89,12 +100,14 @@ bool packageIsNullSafe(File file) { ...@@ -89,12 +100,14 @@ bool packageIsNullSafe(File file) {
} }
Future<int> findGlobalsForFile(File file) async { Future<int> findGlobalsForFile(File file) async {
if (path.extension(file.path) != '.dart') if (path.extension(file.path) != '.dart') {
return 0; return 0;
}
int total = 0; int total = 0;
for (final String line in await file.readAsLines()) { for (final String line in await file.readAsLines()) {
if (line.contains(globalsPattern)) if (line.contains(globalsPattern)) {
total += 1; total += 1;
}
} }
return total; return total;
} }
...@@ -106,11 +119,13 @@ Future<double> findCostsForRepo() async { ...@@ -106,11 +119,13 @@ Future<double> findCostsForRepo() async {
workingDirectory: flutterDirectory.path, workingDirectory: flutterDirectory.path,
); );
double total = 0.0; double total = 0.0;
await for (final String entry in git.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter())) await for (final String entry in git.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter())) {
total += await findCostsForFile(File(path.join(flutterDirectory.path, entry))); total += await findCostsForFile(File(path.join(flutterDirectory.path, entry)));
}
final int gitExitCode = await git.exitCode; final int gitExitCode = await git.exitCode;
if (gitExitCode != 0) if (gitExitCode != 0) {
throw Exception('git exit with unexpected error code $gitExitCode'); throw Exception('git exit with unexpected error code $gitExitCode');
}
return total; return total;
} }
...@@ -121,11 +136,13 @@ Future<int> findGlobalsForTool() async { ...@@ -121,11 +136,13 @@ Future<int> findGlobalsForTool() async {
workingDirectory: flutterDirectory.path, workingDirectory: flutterDirectory.path,
); );
int total = 0; int total = 0;
await for (final String entry in git.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter())) await for (final String entry in git.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter())) {
total += await findGlobalsForFile(File(path.join(flutterDirectory.path, entry))); total += await findGlobalsForFile(File(path.join(flutterDirectory.path, entry)));
}
final int gitExitCode = await git.exitCode; final int gitExitCode = await git.exitCode;
if (gitExitCode != 0) if (gitExitCode != 0) {
throw Exception('git exit with unexpected error code $gitExitCode'); throw Exception('git exit with unexpected error code $gitExitCode');
}
return total; return total;
} }
...@@ -135,8 +152,9 @@ Future<int> countDependencies() async { ...@@ -135,8 +152,9 @@ Future<int> countDependencies() async {
options: <String>['--transitive-closure'], options: <String>['--transitive-closure'],
)).split('\n'); )).split('\n');
final int count = lines.where((String line) => line.contains('->')).length; final int count = lines.where((String line) => line.contains('->')).length;
if (count < 2) // we'll always have flutter and flutter_test, at least... if (count < 2) {
throw Exception('"flutter update-packages --transitive-closure" returned bogus output:\n${lines.join("\n")}'); throw Exception('"flutter update-packages --transitive-closure" returned bogus output:\n${lines.join("\n")}');
}
return count; return count;
} }
...@@ -146,8 +164,9 @@ Future<int> countConsumerDependencies() async { ...@@ -146,8 +164,9 @@ Future<int> countConsumerDependencies() async {
options: <String>['--transitive-closure', '--consumer-only'], options: <String>['--transitive-closure', '--consumer-only'],
)).split('\n'); )).split('\n');
final int count = lines.where((String line) => line.contains('->')).length; final int count = lines.where((String line) => line.contains('->')).length;
if (count < 2) // we'll always have flutter and flutter_test, at least... if (count < 2) {
throw Exception('"flutter update-packages --transitive-closure" returned bogus output:\n${lines.join("\n")}'); throw Exception('"flutter update-packages --transitive-closure" returned bogus output:\n${lines.join("\n")}');
}
return count; return count;
} }
......
...@@ -410,8 +410,9 @@ Future<void> _runGradleTask({ ...@@ -410,8 +410,9 @@ Future<void> _runGradleTask({
print('stderr:'); print('stderr:');
print(result.stderr); print(result.stderr);
} }
if (result.exitCode != 0) if (result.exitCode != 0) {
throw 'Gradle exited with error'; throw 'Gradle exited with error';
}
} }
Future<ProcessResult> _resultOfGradleTask({ Future<ProcessResult> _resultOfGradleTask({
...@@ -422,8 +423,9 @@ Future<ProcessResult> _resultOfGradleTask({ ...@@ -422,8 +423,9 @@ Future<ProcessResult> _resultOfGradleTask({
section('Find Java'); section('Find Java');
final String? javaHome = await findJavaHome(); final String? javaHome = await findJavaHome();
if (javaHome == null) if (javaHome == null) {
throw TaskResult.failure('Could not find Java'); throw TaskResult.failure('Could not find Java');
}
print('\nUsing JAVA_HOME=$javaHome'); print('\nUsing JAVA_HOME=$javaHome');
......
...@@ -252,8 +252,9 @@ class AndroidDeviceDiscovery implements DeviceDiscovery { ...@@ -252,8 +252,9 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
.map<AndroidDevice>((String id) => AndroidDevice(deviceId: id)) .map<AndroidDevice>((String id) => AndroidDevice(deviceId: id))
.toList(); .toList();
if (allDevices.isEmpty) if (allDevices.isEmpty) {
throw const DeviceException('No Android devices detected'); throw const DeviceException('No Android devices detected');
}
if (cpu != null) { if (cpu != null) {
for (final AndroidDevice device in allDevices) { for (final AndroidDevice device in allDevices) {
...@@ -268,8 +269,9 @@ class AndroidDeviceDiscovery implements DeviceDiscovery { ...@@ -268,8 +269,9 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
_workingDevice = allDevices[math.Random().nextInt(allDevices.length)]; _workingDevice = allDevices[math.Random().nextInt(allDevices.length)];
} }
if (_workingDevice == null) if (_workingDevice == null) {
throw const DeviceException('Cannot find a suitable Android device'); throw const DeviceException('Cannot find a suitable Android device');
}
print('Device chosen: $_workingDevice'); print('Device chosen: $_workingDevice');
} }
...@@ -300,11 +302,13 @@ class AndroidDeviceDiscovery implements DeviceDiscovery { ...@@ -300,11 +302,13 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
final List<String> results = <String>[]; final List<String> results = <String>[];
for (final String line in output) { for (final String line in output) {
// Skip lines like: * daemon started successfully * // Skip lines like: * daemon started successfully *
if (line.startsWith('* daemon ')) if (line.startsWith('* daemon ')) {
continue; continue;
}
if (line.startsWith('List of devices')) if (line.startsWith('List of devices')) {
continue; continue;
}
if (_kDeviceRegex.hasMatch(line)) { if (_kDeviceRegex.hasMatch(line)) {
final Match match = _kDeviceRegex.firstMatch(line)!; final Match match = _kDeviceRegex.firstMatch(line)!;
...@@ -551,15 +555,17 @@ class AndroidDevice extends Device { ...@@ -551,15 +555,17 @@ class AndroidDevice extends Device {
/// Wake up the device if it is not awake using [togglePower]. /// Wake up the device if it is not awake using [togglePower].
@override @override
Future<void> wakeUp() async { Future<void> wakeUp() async {
if (!(await isAwake())) if (!(await isAwake())) {
await togglePower(); await togglePower();
}
} }
/// Send the device to sleep mode if it is not asleep using [togglePower]. /// Send the device to sleep mode if it is not asleep using [togglePower].
@override @override
Future<void> sendToSleep() async { Future<void> sendToSleep() async {
if (!(await isAsleep())) if (!(await isAsleep())) {
await togglePower(); await togglePower();
}
} }
/// Sends `KEYCODE_HOME` (3), which causes the device to go to the home screen. /// Sends `KEYCODE_HOME` (3), which causes the device to go to the home screen.
...@@ -840,8 +846,9 @@ class IosDeviceDiscovery implements DeviceDiscovery { ...@@ -840,8 +846,9 @@ class IosDeviceDiscovery implements DeviceDiscovery {
.map<IosDevice>((String id) => IosDevice(deviceId: id)) .map<IosDevice>((String id) => IosDevice(deviceId: id))
.toList(); .toList();
if (allDevices.isEmpty) if (allDevices.isEmpty) {
throw const DeviceException('No iOS devices detected'); throw const DeviceException('No iOS devices detected');
}
// TODO(yjbanov): filter out and warn about those with low battery level // TODO(yjbanov): filter out and warn about those with low battery level
_workingDevice = allDevices[math.Random().nextInt(allDevices.length)]; _workingDevice = allDevices[math.Random().nextInt(allDevices.length)];
...@@ -1211,8 +1218,9 @@ String get adbPath { ...@@ -1211,8 +1218,9 @@ String get adbPath {
final String adbPath = path.join(androidHome, 'platform-tools/adb'); final String adbPath = path.join(androidHome, 'platform-tools/adb');
if (!canRun(adbPath)) if (!canRun(adbPath)) {
throw DeviceException('adb not found at: $adbPath'); throw DeviceException('adb not found at: $adbPath');
}
return path.absolute(adbPath); return path.absolute(adbPath);
} }
......
...@@ -46,8 +46,9 @@ bool _isTaskRegistered = false; ...@@ -46,8 +46,9 @@ bool _isTaskRegistered = false;
/// If no `processManager` is provided, a default [LocalProcessManager] is created /// If no `processManager` is provided, a default [LocalProcessManager] is created
/// for the task. /// for the task.
Future<TaskResult> task(TaskFunction task, { ProcessManager? processManager }) async { Future<TaskResult> task(TaskFunction task, { ProcessManager? processManager }) async {
if (_isTaskRegistered) if (_isTaskRegistered) {
throw StateError('A task is already registered'); throw StateError('A task is already registered');
}
_isTaskRegistered = true; _isTaskRegistered = true;
processManager ??= const LocalProcessManager(); processManager ??= const LocalProcessManager();
...@@ -163,8 +164,9 @@ class _TaskRunner { ...@@ -163,8 +164,9 @@ class _TaskRunner {
} }
Future<TaskResult> futureResult = _performTask(); Future<TaskResult> futureResult = _performTask();
if (taskTimeout != null) if (taskTimeout != null) {
futureResult = futureResult.timeout(taskTimeout); futureResult = futureResult.timeout(taskTimeout);
}
result = await futureResult; result = await futureResult;
} finally { } finally {
...@@ -241,8 +243,9 @@ class _TaskRunner { ...@@ -241,8 +243,9 @@ class _TaskRunner {
/// Causes the Dart VM to stay alive until a request to run the task is /// Causes the Dart VM to stay alive until a request to run the task is
/// received via the VM service protocol. /// received via the VM service protocol.
void keepVmAliveUntilTaskRunRequested() { void keepVmAliveUntilTaskRunRequested() {
if (_taskStarted) if (_taskStarted) {
throw StateError('Task already started.'); throw StateError('Task already started.');
}
// Merely creating this port object will cause the VM to stay alive and keep // Merely creating this port object will cause the VM to stay alive and keep
// the VM service server running until the port is disposed of. // the VM service server running until the port is disposed of.
...@@ -280,8 +283,9 @@ class _TaskRunner { ...@@ -280,8 +283,9 @@ class _TaskRunner {
// are catching errors coming from arbitrary (and untrustworthy) task // are catching errors coming from arbitrary (and untrustworthy) task
// code. Our goal is to convert the failure into a readable message. // code. Our goal is to convert the failure into a readable message.
// Propagating it further is not useful. // Propagating it further is not useful.
if (!completer.isCompleted) if (!completer.isCompleted) {
completer.complete(TaskResult.failure(message)); completer.complete(TaskResult.failure(message));
}
}); });
return completer.future; return completer.future;
} }
......
...@@ -156,8 +156,9 @@ Future<TaskResult> runTask( ...@@ -156,8 +156,9 @@ Future<TaskResult> runTask(
}) async { }) async {
final String taskExecutable = 'bin/tasks/$taskName.dart'; final String taskExecutable = 'bin/tasks/$taskName.dart';
if (!file(taskExecutable).existsSync()) if (!file(taskExecutable).existsSync()) {
throw 'Executable Dart file not found: $taskExecutable'; throw 'Executable Dart file not found: $taskExecutable';
}
final Process runner = await startProcess( final Process runner = await startProcess(
dartBin, dartBin,
...@@ -190,8 +191,9 @@ Future<TaskResult> runTask( ...@@ -190,8 +191,9 @@ Future<TaskResult> runTask(
.listen((String line) { .listen((String line) {
if (!uri.isCompleted) { if (!uri.isCompleted) {
final Uri? serviceUri = parseServiceUri(line, prefix: RegExp('(Observatory|The Dart VM service is) listening on ')); final Uri? serviceUri = parseServiceUri(line, prefix: RegExp('(Observatory|The Dart VM service is) listening on '));
if (serviceUri != null) if (serviceUri != null) {
uri.complete(serviceUri); uri.complete(serviceUri);
}
} }
if (!silent) { if (!silent) {
stdout.writeln('[$taskName] [STDOUT] $line'); stdout.writeln('[$taskName] [STDOUT] $line');
...@@ -255,12 +257,14 @@ Future<ConnectionResult> _connectToRunnerIsolate(Uri vmServiceUri) async { ...@@ -255,12 +257,14 @@ Future<ConnectionResult> _connectToRunnerIsolate(Uri vmServiceUri) async {
} }
final IsolateRef isolate = vm.isolates!.first; final IsolateRef isolate = vm.isolates!.first;
final Response response = await client.callServiceExtension('ext.cocoonRunnerReady', isolateId: isolate.id); final Response response = await client.callServiceExtension('ext.cocoonRunnerReady', isolateId: isolate.id);
if (response.json!['response'] != 'ready') if (response.json!['response'] != 'ready') {
throw 'not ready yet'; throw 'not ready yet';
}
return ConnectionResult(client, isolate); return ConnectionResult(client, isolate);
} catch (error) { } catch (error) {
if (stopwatch.elapsed > const Duration(seconds: 10)) if (stopwatch.elapsed > const Duration(seconds: 10)) {
print('VM service still not ready after ${stopwatch.elapsed}: $error\nContinuing to retry...'); print('VM service still not ready after ${stopwatch.elapsed}: $error\nContinuing to retry...');
}
await Future<void>.delayed(const Duration(milliseconds: 50)); await Future<void>.delayed(const Duration(milliseconds: 50));
} }
} }
......
...@@ -126,14 +126,15 @@ void copy(File sourceFile, Directory targetDirectory, {String? name}) { ...@@ -126,14 +126,15 @@ void copy(File sourceFile, Directory targetDirectory, {String? name}) {
} }
void recursiveCopy(Directory source, Directory target) { void recursiveCopy(Directory source, Directory target) {
if (!target.existsSync()) if (!target.existsSync()) {
target.createSync(); target.createSync();
}
for (final FileSystemEntity entity in source.listSync(followLinks: false)) { for (final FileSystemEntity entity in source.listSync(followLinks: false)) {
final String name = path.basename(entity.path); final String name = path.basename(entity.path);
if (entity is Directory && !entity.path.contains('.dart_tool')) if (entity is Directory && !entity.path.contains('.dart_tool')) {
recursiveCopy(entity, Directory(path.join(target.path, name))); recursiveCopy(entity, Directory(path.join(target.path, name)));
else if (entity is File) { } else if (entity is File) {
final File dest = File(path.join(target.path, name)); final File dest = File(path.join(target.path, name));
dest.writeAsBytesSync(entity.readAsBytesSync()); dest.writeAsBytesSync(entity.readAsBytesSync());
// Preserve executable bit // Preserve executable bit
...@@ -194,8 +195,9 @@ void section(String title) { ...@@ -194,8 +195,9 @@ void section(String title) {
title = '╡ ••• $title ••• ╞'; title = '╡ ••• $title ••• ╞';
final String line = '═' * math.max((80 - title.length) ~/ 2, 2); final String line = '═' * math.max((80 - title.length) ~/ 2, 2);
output = '$line$title$line'; output = '$line$title$line';
if (output.length == 79) if (output.length == 79) {
output += '═'; output += '═';
}
} }
print('\n\n$output\n'); print('\n\n$output\n');
} }
...@@ -209,10 +211,12 @@ Future<String> getDartVersion() async { ...@@ -209,10 +211,12 @@ Future<String> getDartVersion() async {
// Dart VM version: 1.17.0-dev.2.0 (Tue May 3 12:14:52 2016) on "macos_x64" // Dart VM version: 1.17.0-dev.2.0 (Tue May 3 12:14:52 2016) on "macos_x64"
// to: // to:
// 1.17.0-dev.2.0 // 1.17.0-dev.2.0
if (version.contains('(')) if (version.contains('(')) {
version = version.substring(0, version.indexOf('(')).trim(); version = version.substring(0, version.indexOf('(')).trim();
if (version.contains(':')) }
if (version.contains(':')) {
version = version.substring(version.indexOf(':') + 1).trim(); version = version.substring(version.indexOf(':') + 1).trim();
}
return version.replaceAll('"', "'"); return version.replaceAll('"', "'");
} }
...@@ -295,8 +299,9 @@ Future<Process> startProcess( ...@@ -295,8 +299,9 @@ Future<Process> startProcess(
} }
Future<void> forceQuitRunningProcesses() async { Future<void> forceQuitRunningProcesses() async {
if (_runningProcesses.isEmpty) if (_runningProcesses.isEmpty) {
return; return;
}
// Give normally quitting processes a chance to report their exit code. // Give normally quitting processes a chance to report their exit code.
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
...@@ -354,8 +359,9 @@ Future<int> _execute( ...@@ -354,8 +359,9 @@ Future<int> _execute(
); );
final int exitCode = await process.exitCode; final int exitCode = await process.exitCode;
if (exitCode != 0 && !canFail) if (exitCode != 0 && !canFail) {
fail('Executable "$executable" failed with exit code $exitCode.'); fail('Executable "$executable" failed with exit code $exitCode.');
}
return exitCode; return exitCode;
} }
...@@ -545,8 +551,9 @@ Future<String?> findJavaHome() async { ...@@ -545,8 +551,9 @@ Future<String?> findJavaHome() async {
'Java binary at: ', 'Java binary at: ',
from: await evalFlutter('doctor', options: <String>['-v']), from: await evalFlutter('doctor', options: <String>['-v']),
); );
if (hits.isEmpty) if (hits.isEmpty) {
return null; return null;
}
final String javaBinary = hits.first final String javaBinary = hits.first
.split(': ') .split(': ')
.last; .last;
...@@ -579,8 +586,9 @@ void cd(dynamic directory) { ...@@ -579,8 +586,9 @@ void cd(dynamic directory) {
throw FileSystemException('Unsupported directory type ${directory.runtimeType}', directory.toString()); throw FileSystemException('Unsupported directory type ${directory.runtimeType}', directory.toString());
} }
if (!d.existsSync()) if (!d.existsSync()) {
throw FileSystemException('Cannot cd into directory that does not exist', d.toString()); throw FileSystemException('Cannot cd into directory that does not exist', d.toString());
}
} }
Directory get flutterDirectory => Directory.current.parent.parent; Directory get flutterDirectory => Directory.current.parent.parent;
...@@ -590,15 +598,17 @@ Directory get openpayDirectory => Directory(requireEnvVar('OPENPAY_CHECKOUT_PATH ...@@ -590,15 +598,17 @@ Directory get openpayDirectory => Directory(requireEnvVar('OPENPAY_CHECKOUT_PATH
String requireEnvVar(String name) { String requireEnvVar(String name) {
final String? value = Platform.environment[name]; final String? value = Platform.environment[name];
if (value == null) if (value == null) {
fail('$name environment variable is missing. Quitting.'); fail('$name environment variable is missing. Quitting.');
}
return value!; return value!;
} }
T requireConfigProperty<T>(Map<String, dynamic> map, String propertyName) { T requireConfigProperty<T>(Map<String, dynamic> map, String propertyName) {
if (!map.containsKey(propertyName)) if (!map.containsKey(propertyName)) {
fail('Configuration property not found: $propertyName'); fail('Configuration property not found: $propertyName');
}
final T result = map[propertyName] as T; final T result = map[propertyName] as T;
return result; return result;
} }
...@@ -634,26 +644,36 @@ void checkNotNull(Object o1, ...@@ -634,26 +644,36 @@ void checkNotNull(Object o1,
Object o8 = 1, Object o8 = 1,
Object o9 = 1, Object o9 = 1,
Object o10 = 1]) { Object o10 = 1]) {
if (o1 == null) if (o1 == null) {
throw 'o1 is null'; throw 'o1 is null';
if (o2 == null) }
if (o2 == null) {
throw 'o2 is null'; throw 'o2 is null';
if (o3 == null) }
if (o3 == null) {
throw 'o3 is null'; throw 'o3 is null';
if (o4 == null) }
if (o4 == null) {
throw 'o4 is null'; throw 'o4 is null';
if (o5 == null) }
if (o5 == null) {
throw 'o5 is null'; throw 'o5 is null';
if (o6 == null) }
if (o6 == null) {
throw 'o6 is null'; throw 'o6 is null';
if (o7 == null) }
if (o7 == null) {
throw 'o7 is null'; throw 'o7 is null';
if (o8 == null) }
if (o8 == null) {
throw 'o8 is null'; throw 'o8 is null';
if (o9 == null) }
if (o9 == null) {
throw 'o9 is null'; throw 'o9 is null';
if (o10 == null) }
if (o10 == null) {
throw 'o10 is null'; throw 'o10 is null';
}
} }
/// Splits [from] into lines and selects those that contain [pattern]. /// Splits [from] into lines and selects those that contain [pattern].
......
...@@ -74,8 +74,9 @@ Future<Map<String, double>> readJsonResults(Process process) { ...@@ -74,8 +74,9 @@ Future<Map<String, double>> readJsonResults(Process process) {
return; return;
} }
if (jsonStarted && line.contains(jsonPrefix)) if (jsonStarted && line.contains(jsonPrefix)) {
jsonBuf.writeln(line.substring(line.indexOf(jsonPrefix) + jsonPrefix.length)); jsonBuf.writeln(line.substring(line.indexOf(jsonPrefix) + jsonPrefix.length));
}
}); });
process.exitCode.then<void>((int code) async { process.exitCode.then<void>((int code) async {
......
...@@ -288,8 +288,9 @@ TaskFunction createBasicMaterialCompileTest() { ...@@ -288,8 +288,9 @@ TaskFunction createBasicMaterialCompileTest() {
await flutter('create', options: <String>['--template=app', sampleAppName]); await flutter('create', options: <String>['--template=app', sampleAppName]);
}); });
if (!sampleDir.existsSync()) if (!sampleDir.existsSync()) {
throw 'Failed to create default Flutter app in ${sampleDir.path}'; throw 'Failed to create default Flutter app in ${sampleDir.path}';
}
return CompileTest(sampleDir.path).run(); return CompileTest(sampleDir.path).run();
}; };
...@@ -754,8 +755,9 @@ class StartupTest { ...@@ -754,8 +755,9 @@ class StartupTest {
final Map<String, dynamic> averageResults = _average(results, iterations); final Map<String, dynamic> averageResults = _average(results, iterations);
if (!reportMetrics) if (!reportMetrics) {
return TaskResult.success(averageResults); return TaskResult.success(averageResults);
}
return TaskResult.success(averageResults, benchmarkScoreKeys: <String>[ return TaskResult.success(averageResults, benchmarkScoreKeys: <String>[
'timeToFirstFrameMicros', 'timeToFirstFrameMicros',
...@@ -860,8 +862,9 @@ class DevtoolsStartupTest { ...@@ -860,8 +862,9 @@ class DevtoolsStartupTest {
device.deviceId, device.deviceId,
]); ]);
if (sawLine) if (sawLine) {
return TaskResult.success(null, benchmarkScoreKeys: <String>[]); return TaskResult.success(null, benchmarkScoreKeys: <String>[]);
}
return TaskResult.failure('Did not see line "The Flutter DevTools debugger and profiler" in output'); return TaskResult.failure('Did not see line "The Flutter DevTools debugger and profiler" in output');
}); });
} }
...@@ -1401,8 +1404,9 @@ class CompileTest { ...@@ -1401,8 +1404,9 @@ class CompileTest {
// IPAs are created manually, https://flutter.dev/ios-release/ // IPAs are created manually, https://flutter.dev/ios-release/
await exec('tar', <String>['-zcf', 'build/app.ipa', appPath]); await exec('tar', <String>['-zcf', 'build/app.ipa', appPath]);
releaseSizeInBytes = await file('$cwd/build/app.ipa').length(); releaseSizeInBytes = await file('$cwd/build/app.ipa').length();
if (reportPackageContentSizes) if (reportPackageContentSizes) {
metrics.addAll(await getSizesFromIosApp(appPath)); metrics.addAll(await getSizesFromIosApp(appPath));
}
break; break;
case DeviceOperatingSystem.android: case DeviceOperatingSystem.android:
case DeviceOperatingSystem.androidArm: case DeviceOperatingSystem.androidArm:
...@@ -1416,8 +1420,9 @@ class CompileTest { ...@@ -1416,8 +1420,9 @@ class CompileTest {
final String apkPath = '$cwd/build/app/outputs/flutter-apk/app-release.apk'; final String apkPath = '$cwd/build/app/outputs/flutter-apk/app-release.apk';
final File apk = file(apkPath); final File apk = file(apkPath);
releaseSizeInBytes = apk.lengthSync(); releaseSizeInBytes = apk.lengthSync();
if (reportPackageContentSizes) if (reportPackageContentSizes) {
metrics.addAll(await getSizesFromApk(apkPath)); metrics.addAll(await getSizesFromApk(apkPath));
}
break; break;
case DeviceOperatingSystem.androidArm64: case DeviceOperatingSystem.androidArm64:
options.insert(0, 'apk'); options.insert(0, 'apk');
...@@ -1430,8 +1435,9 @@ class CompileTest { ...@@ -1430,8 +1435,9 @@ class CompileTest {
final String apkPath = '$cwd/build/app/outputs/flutter-apk/app-release.apk'; final String apkPath = '$cwd/build/app/outputs/flutter-apk/app-release.apk';
final File apk = file(apkPath); final File apk = file(apkPath);
releaseSizeInBytes = apk.lengthSync(); releaseSizeInBytes = apk.lengthSync();
if (reportPackageContentSizes) if (reportPackageContentSizes) {
metrics.addAll(await getSizesFromApk(apkPath)); metrics.addAll(await getSizesFromApk(apkPath));
}
break; break;
case DeviceOperatingSystem.fake: case DeviceOperatingSystem.fake:
throw Exception('Unsupported option for fake devices'); throw Exception('Unsupported option for fake devices');
...@@ -1572,8 +1578,9 @@ class MemoryTest { ...@@ -1572,8 +1578,9 @@ class MemoryTest {
final StreamSubscription<String> adb = device!.logcat.listen( final StreamSubscription<String> adb = device!.logcat.listen(
(String data) { (String data) {
if (data.contains('==== MEMORY BENCHMARK ==== $_nextMessage ====')) if (data.contains('==== MEMORY BENCHMARK ==== $_nextMessage ====')) {
_receivedNextMessage?.complete(); _receivedNextMessage?.complete();
}
}, },
); );
...@@ -1764,8 +1771,9 @@ class ReportedDurationTest { ...@@ -1764,8 +1771,9 @@ class ReportedDurationTest {
final StreamSubscription<String> adb = device!.logcat.listen( final StreamSubscription<String> adb = device!.logcat.listen(
(String data) { (String data) {
if (durationPattern.hasMatch(data)) if (durationPattern.hasMatch(data)) {
durationCompleter.complete(int.parse(durationPattern.firstMatch(data)!.group(1)!)); durationCompleter.complete(int.parse(durationPattern.firstMatch(data)!.group(1)!));
}
}, },
); );
print('launching $project$test on device...'); print('launching $project$test on device...');
......
...@@ -158,8 +158,9 @@ class CommandArgs { ...@@ -158,8 +158,9 @@ class CommandArgs {
@override @override
bool operator==(Object other) { bool operator==(Object other) {
if (other.runtimeType != CommandArgs) if (other.runtimeType != CommandArgs) {
return false; return false;
}
return other is CommandArgs return other is CommandArgs
&& other.command == command && other.command == command
&& const ListEquality<String>().equals(other.arguments, arguments) && const ListEquality<String>().equals(other.arguments, arguments)
......
...@@ -33,10 +33,11 @@ Future<String> dataHandler(String message) async { ...@@ -33,10 +33,11 @@ Future<String> dataHandler(String message) async {
}); });
completer.complete(json.encode(result)); completer.complete(json.encode(result));
} }
if (SchedulerBinding.instance.hasScheduledFrame) if (SchedulerBinding.instance.hasScheduledFrame) {
SchedulerBinding.instance.addPostFrameCallback(completeSemantics); SchedulerBinding.instance.addPostFrameCallback(completeSemantics);
else } else {
completeSemantics(); completeSemantics();
}
return completer.future; return completer.future;
} }
if (message.contains('setClipboard')) { if (message.contains('setClipboard')) {
...@@ -48,10 +49,11 @@ Future<String> dataHandler(String message) async { ...@@ -48,10 +49,11 @@ Future<String> dataHandler(String message) async {
}); });
completer.complete(''); completer.complete('');
} }
if (SchedulerBinding.instance.hasScheduledFrame) if (SchedulerBinding.instance.hasScheduledFrame) {
SchedulerBinding.instance.addPostFrameCallback(completeSetClipboard); SchedulerBinding.instance.addPostFrameCallback(completeSetClipboard);
else } else {
completeSetClipboard(); completeSetClipboard();
}
return completer.future; return completer.future;
} }
throw UnimplementedError(); throw UnimplementedError();
......
...@@ -187,8 +187,9 @@ class Rect { ...@@ -187,8 +187,9 @@ class Rect {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is Rect return other is Rect
&& other.top == top && other.top == top
&& other.left == left && other.left == left
...@@ -219,8 +220,9 @@ class Size { ...@@ -219,8 +220,9 @@ class Size {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is Size return other is Size
&& other.width == width && other.width == width
&& other.height == height; && other.height == height;
......
...@@ -201,8 +201,9 @@ class AndroidSemanticsAction { ...@@ -201,8 +201,9 @@ class AndroidSemanticsAction {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is AndroidSemanticsAction return other is AndroidSemanticsAction
&& other.id == id; && other.id == id;
} }
......
...@@ -100,53 +100,74 @@ class _AndroidSemanticsMatcher extends Matcher { ...@@ -100,53 +100,74 @@ class _AndroidSemanticsMatcher extends Matcher {
@override @override
Description describe(Description description) { Description describe(Description description) {
description.add('AndroidSemanticsNode'); description.add('AndroidSemanticsNode');
if (text != null) if (text != null) {
description.add(' with text: $text'); description.add(' with text: $text');
if (contentDescription != null) }
if (contentDescription != null) {
description.add( 'with contentDescription $contentDescription'); description.add( 'with contentDescription $contentDescription');
if (className != null) }
if (className != null) {
description.add(' with className: $className'); description.add(' with className: $className');
if (id != null) }
if (id != null) {
description.add(' with id: $id'); description.add(' with id: $id');
if (actions != null) }
if (actions != null) {
description.add(' with actions: $actions'); description.add(' with actions: $actions');
if (rect != null) }
if (rect != null) {
description.add(' with rect: $rect'); description.add(' with rect: $rect');
if (size != null) }
if (size != null) {
description.add(' with size: $size'); description.add(' with size: $size');
if (isChecked != null) }
if (isChecked != null) {
description.add(' with flag isChecked: $isChecked'); description.add(' with flag isChecked: $isChecked');
if (isEditable != null) }
if (isEditable != null) {
description.add(' with flag isEditable: $isEditable'); description.add(' with flag isEditable: $isEditable');
if (isEnabled != null) }
if (isEnabled != null) {
description.add(' with flag isEnabled: $isEnabled'); description.add(' with flag isEnabled: $isEnabled');
if (isFocusable != null) }
if (isFocusable != null) {
description.add(' with flag isFocusable: $isFocusable'); description.add(' with flag isFocusable: $isFocusable');
if (isFocused != null) }
if (isFocused != null) {
description.add(' with flag isFocused: $isFocused'); description.add(' with flag isFocused: $isFocused');
if (isHeading != null) }
if (isHeading != null) {
description.add(' with flag isHeading: $isHeading'); description.add(' with flag isHeading: $isHeading');
if (isPassword != null) }
if (isPassword != null) {
description.add(' with flag isPassword: $isPassword'); description.add(' with flag isPassword: $isPassword');
if (isLongClickable != null) }
if (isLongClickable != null) {
description.add(' with flag isLongClickable: $isLongClickable'); description.add(' with flag isLongClickable: $isLongClickable');
}
return description; return description;
} }
@override @override
bool matches(covariant AndroidSemanticsNode item, Map<Object, Object> matchState) { bool matches(covariant AndroidSemanticsNode item, Map<Object, Object> matchState) {
if (text != null && text != item.text) if (text != null && text != item.text) {
return _failWithMessage('Expected text: $text', matchState); return _failWithMessage('Expected text: $text', matchState);
if (contentDescription != null && contentDescription != item.contentDescription) }
if (contentDescription != null && contentDescription != item.contentDescription) {
return _failWithMessage('Expected contentDescription: $contentDescription', matchState); return _failWithMessage('Expected contentDescription: $contentDescription', matchState);
if (className != null && className != item.className) }
if (className != null && className != item.className) {
return _failWithMessage('Expected className: $className', matchState); return _failWithMessage('Expected className: $className', matchState);
if (id != null && id != item.id) }
if (id != null && id != item.id) {
return _failWithMessage('Expected id: $id', matchState); return _failWithMessage('Expected id: $id', matchState);
if (rect != null && rect != item.getRect()) }
if (rect != null && rect != item.getRect()) {
return _failWithMessage('Expected rect: $rect', matchState); return _failWithMessage('Expected rect: $rect', matchState);
if (size != null && size != item.getSize()) }
if (size != null && size != item.getSize()) {
return _failWithMessage('Expected size: $size', matchState); return _failWithMessage('Expected size: $size', matchState);
}
if (actions != null) { if (actions != null) {
final List<AndroidSemanticsAction> itemActions = item.getActions(); final List<AndroidSemanticsAction> itemActions = item.getActions();
if (!unorderedEquals(actions).matches(itemActions, matchState)) { if (!unorderedEquals(actions).matches(itemActions, matchState)) {
...@@ -161,25 +182,34 @@ class _AndroidSemanticsMatcher extends Matcher { ...@@ -161,25 +182,34 @@ class _AndroidSemanticsMatcher extends Matcher {
return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpectedInString\nMissing: $missingInString', matchState); return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpectedInString\nMissing: $missingInString', matchState);
} }
} }
if (isChecked != null && isChecked != item.isChecked) if (isChecked != null && isChecked != item.isChecked) {
return _failWithMessage('Expected isChecked: $isChecked', matchState); return _failWithMessage('Expected isChecked: $isChecked', matchState);
if (isCheckable != null && isCheckable != item.isCheckable) }
if (isCheckable != null && isCheckable != item.isCheckable) {
return _failWithMessage('Expected isCheckable: $isCheckable', matchState); return _failWithMessage('Expected isCheckable: $isCheckable', matchState);
if (isEditable != null && isEditable != item.isEditable) }
if (isEditable != null && isEditable != item.isEditable) {
return _failWithMessage('Expected isEditable: $isEditable', matchState); return _failWithMessage('Expected isEditable: $isEditable', matchState);
if (isEnabled != null && isEnabled != item.isEnabled) }
if (isEnabled != null && isEnabled != item.isEnabled) {
return _failWithMessage('Expected isEnabled: $isEnabled', matchState); return _failWithMessage('Expected isEnabled: $isEnabled', matchState);
if (isFocusable != null && isFocusable != item.isFocusable) }
if (isFocusable != null && isFocusable != item.isFocusable) {
return _failWithMessage('Expected isFocusable: $isFocusable', matchState); return _failWithMessage('Expected isFocusable: $isFocusable', matchState);
if (isFocused != null && isFocused != item.isFocused) }
if (isFocused != null && isFocused != item.isFocused) {
return _failWithMessage('Expected isFocused: $isFocused', matchState); return _failWithMessage('Expected isFocused: $isFocused', matchState);
}
// Heading is not available in all Android versions, so match anything if it is not set by the platform // Heading is not available in all Android versions, so match anything if it is not set by the platform
if (isHeading != null && isHeading != item.isHeading && item.isHeading != null) if (isHeading != null && isHeading != item.isHeading && item.isHeading != null) {
return _failWithMessage('Expected isHeading: $isHeading', matchState); return _failWithMessage('Expected isHeading: $isHeading', matchState);
if (isPassword != null && isPassword != item.isPassword) }
if (isPassword != null && isPassword != item.isPassword) {
return _failWithMessage('Expected isPassword: $isPassword', matchState); return _failWithMessage('Expected isPassword: $isPassword', matchState);
if (isLongClickable != null && isLongClickable != item.isLongClickable) }
if (isLongClickable != null && isLongClickable != item.isLongClickable) {
return _failWithMessage('Expected longClickable: $isLongClickable', matchState); return _failWithMessage('Expected longClickable: $isLongClickable', matchState);
}
return true; return true;
} }
......
...@@ -47,17 +47,19 @@ void diffActions(StringBuffer diffBuffer, Map<String, dynamic> originalEvent, ...@@ -47,17 +47,19 @@ void diffActions(StringBuffer diffBuffer, Map<String, dynamic> originalEvent,
final String originalActionName = final String originalActionName =
getActionName(originalActionMasked, originalEvent['action'] as int); getActionName(originalActionMasked, originalEvent['action'] as int);
if (synthesizedActionMasked != originalActionMasked) if (synthesizedActionMasked != originalActionMasked) {
diffBuffer.write( diffBuffer.write(
'action (expected: $originalActionName actual: $synthesizedActionName) '); 'action (expected: $originalActionName actual: $synthesizedActionName) ');
}
if (kPointerActions.contains(originalActionMasked) && if (kPointerActions.contains(originalActionMasked) &&
originalActionMasked == synthesizedActionMasked) { originalActionMasked == synthesizedActionMasked) {
final int originalPointer = getPointerIdx(originalEvent['action'] as int); final int originalPointer = getPointerIdx(originalEvent['action'] as int);
final int synthesizedPointer = getPointerIdx(synthesizedEvent['action'] as int); final int synthesizedPointer = getPointerIdx(synthesizedEvent['action'] as int);
if (originalPointer != synthesizedPointer) if (originalPointer != synthesizedPointer) {
diffBuffer.write( diffBuffer.write(
'pointerIdx (expected: $originalPointer actual: $synthesizedPointer action: $originalActionName '); 'pointerIdx (expected: $originalPointer actual: $synthesizedPointer action: $originalActionName ');
}
} }
} }
...@@ -123,10 +125,12 @@ void diffMaps( ...@@ -123,10 +125,12 @@ void diffMaps(
return; return;
} }
for (final String key in expected.keys) { for (final String key in expected.keys) {
if (excludeKeys.contains(key)) if (excludeKeys.contains(key)) {
continue; continue;
if (doublesApproximatelyMatch(expected[key], actual[key])) }
if (doublesApproximatelyMatch(expected[key], actual[key])) {
continue; continue;
}
if (expected[key] != actual[key]) { if (expected[key] != actual[key]) {
diffBuffer.write( diffBuffer.write(
...@@ -155,10 +159,11 @@ String getActionName(int actionMasked, int action) { ...@@ -155,10 +159,11 @@ String getActionName(int actionMasked, int action) {
'BUTTON_PRESS', 'BUTTON_PRESS',
'BUTTON_RELEASE', 'BUTTON_RELEASE',
]; ];
if (actionMasked < actionNames.length) if (actionMasked < actionNames.length) {
return '${actionNames[actionMasked]}($action)'; return '${actionNames[actionMasked]}($action)';
else } else {
return 'ACTION_$actionMasked'; return 'ACTION_$actionMasked';
}
} }
bool doublesApproximatelyMatch(dynamic a, dynamic b) => bool doublesApproximatelyMatch(dynamic a, dynamic b) =>
......
...@@ -146,16 +146,19 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -146,16 +146,19 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
await channel.invokeMethod<void>('stopFlutterViewEvents'); await channel.invokeMethod<void>('stopFlutterViewEvents');
await viewChannel?.invokeMethod<void>('stopTouchEvents'); await viewChannel?.invokeMethod<void>('stopTouchEvents');
if (flutterViewEvents.length != embeddedViewEvents.length) if (flutterViewEvents.length != embeddedViewEvents.length) {
return 'Synthesized ${flutterViewEvents.length} events but the embedded view received ${embeddedViewEvents.length} events'; return 'Synthesized ${flutterViewEvents.length} events but the embedded view received ${embeddedViewEvents.length} events';
}
final StringBuffer diff = StringBuffer(); final StringBuffer diff = StringBuffer();
for (int i = 0; i < flutterViewEvents.length; ++i) { for (int i = 0; i < flutterViewEvents.length; ++i) {
final String currentDiff = diffMotionEvents(flutterViewEvents[i], embeddedViewEvents[i]); final String currentDiff = diffMotionEvents(flutterViewEvents[i], embeddedViewEvents[i]);
if (currentDiff.isEmpty) if (currentDiff.isEmpty) {
continue; continue;
if (diff.isNotEmpty) }
if (diff.isNotEmpty) {
diff.write(', '); diff.write(', ');
}
diff.write(currentDiff); diff.write(currentDiff);
} }
return diff.toString(); return diff.toString();
...@@ -229,8 +232,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -229,8 +232,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
case 'onTouch': case 'onTouch':
final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>; final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>;
flutterViewEvents.insert(0, map.cast<String, dynamic>()); flutterViewEvents.insert(0, map.cast<String, dynamic>());
if (flutterViewEvents.length > kEventsBufferSize) if (flutterViewEvents.length > kEventsBufferSize) {
flutterViewEvents.removeLast(); flutterViewEvents.removeLast();
}
setState(() {}); setState(() {});
break; break;
} }
...@@ -242,8 +246,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -242,8 +246,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
case 'onTouch': case 'onTouch':
final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>; final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>;
embeddedViewEvents.insert(0, map.cast<String, dynamic>()); embeddedViewEvents.insert(0, map.cast<String, dynamic>());
if (embeddedViewEvents.length > kEventsBufferSize) if (embeddedViewEvents.length > kEventsBufferSize) {
embeddedViewEvents.removeLast(); embeddedViewEvents.removeLast();
}
setState(() {}); setState(() {});
break; break;
} }
...@@ -251,9 +256,10 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -251,9 +256,10 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
} }
Widget buildEventTile(BuildContext context, int index) { Widget buildEventTile(BuildContext context, int index) {
if (embeddedViewEvents.length > index) if (embeddedViewEvents.length > index) {
return TouchEventDiff( return TouchEventDiff(
flutterViewEvents[index], embeddedViewEvents[index]); flutterViewEvents[index], embeddedViewEvents[index]);
}
return Text( return Text(
'Unmatched event, action: ${flutterViewEvents[index]['action']}'); 'Unmatched event, action: ${flutterViewEvents[index]['action']}');
} }
......
...@@ -172,10 +172,11 @@ class _TestAppState extends State<TestApp> { ...@@ -172,10 +172,11 @@ class _TestAppState extends State<TestApp> {
void _executeNextStep() { void _executeNextStep() {
setState(() { setState(() {
if (_step < steps.length) if (_step < steps.length) {
_result = steps[_step++](); _result = steps[_step++]();
else } else {
_result = Future<TestStepResult>.value(TestStepResult.complete); _result = Future<TestStepResult>.value(TestStepResult.complete);
}
}); });
} }
......
...@@ -169,10 +169,11 @@ Future<TestStepResult> _basicMessageToUnknownChannel<T>( ...@@ -169,10 +169,11 @@ Future<TestStepResult> _basicMessageToUnknownChannel<T>(
} }
String toString(dynamic message) { String toString(dynamic message) {
if (message is ByteData) if (message is ByteData) {
return message.buffer return message.buffer
.asUint8List(message.offsetInBytes, message.lengthInBytes) .asUint8List(message.offsetInBytes, message.lengthInBytes)
.toString(); .toString();
else } else {
return '$message'; return '$message';
}
} }
...@@ -101,8 +101,9 @@ Future<TestStepResult> resultOfHandshake( ...@@ -101,8 +101,9 @@ Future<TestStepResult> resultOfHandshake(
dynamic error, dynamic error,
) async { ) async {
assert(message != nothing); assert(message != nothing);
while (received.length < 2) while (received.length < 2) {
received.add(nothing); received.add(nothing);
}
TestStatus status; TestStatus status;
if (!_deepEquals(messageEcho, message) || if (!_deepEquals(messageEcho, message) ||
received.length != 2 || received.length != 2 ||
...@@ -127,27 +128,34 @@ Future<TestStepResult> resultOfHandshake( ...@@ -127,27 +128,34 @@ Future<TestStepResult> resultOfHandshake(
} }
String _toString(dynamic message) { String _toString(dynamic message) {
if (message is ByteData) if (message is ByteData) {
return message.buffer return message.buffer
.asUint8List(message.offsetInBytes, message.lengthInBytes) .asUint8List(message.offsetInBytes, message.lengthInBytes)
.toString(); .toString();
else } else {
return '$message'; return '$message';
}
} }
bool _deepEquals(dynamic a, dynamic b) { bool _deepEquals(dynamic a, dynamic b) {
if (a == b) if (a == b) {
return true; return true;
if (a is double && a.isNaN) }
if (a is double && a.isNaN) {
return b is double && b.isNaN; return b is double && b.isNaN;
if (a is ByteData) }
if (a is ByteData) {
return b is ByteData && _deepEqualsByteData(a, b); return b is ByteData && _deepEqualsByteData(a, b);
if (a is List) }
if (a is List) {
return b is List && _deepEqualsList(a, b); return b is List && _deepEqualsList(a, b);
if (a is Map) }
if (a is Map) {
return b is Map && _deepEqualsMap(a, b); return b is Map && _deepEqualsMap(a, b);
if (a is Pair) }
if (a is Pair) {
return b is Pair && _deepEqualsPair(a, b); return b is Pair && _deepEqualsPair(a, b);
}
return false; return false;
} }
...@@ -159,21 +167,25 @@ bool _deepEqualsByteData(ByteData a, ByteData b) { ...@@ -159,21 +167,25 @@ bool _deepEqualsByteData(ByteData a, ByteData b) {
} }
bool _deepEqualsList(List<dynamic> a, List<dynamic> b) { bool _deepEqualsList(List<dynamic> a, List<dynamic> b) {
if (a.length != b.length) if (a.length != b.length) {
return false; return false;
}
for (int i = 0; i < a.length; i++) { for (int i = 0; i < a.length; i++) {
if (!_deepEquals(a[i], b[i])) if (!_deepEquals(a[i], b[i])) {
return false; return false;
}
} }
return true; return true;
} }
bool _deepEqualsMap(Map<dynamic, dynamic> a, Map<dynamic, dynamic> b) { bool _deepEqualsMap(Map<dynamic, dynamic> a, Map<dynamic, dynamic> b) {
if (a.length != b.length) if (a.length != b.length) {
return false; return false;
}
for (final dynamic key in a.keys) { for (final dynamic key in a.keys) {
if (!b.containsKey(key) || !_deepEquals(a[key], b[key])) if (!b.containsKey(key) || !_deepEquals(a[key], b[key])) {
return false; return false;
}
} }
return true; return true;
} }
......
...@@ -115,8 +115,9 @@ Press play to produce texture frames.'''; ...@@ -115,8 +115,9 @@ Press play to produce texture frames.''';
_state = FrameState.initial; _state = FrameState.initial;
}); });
} else { } else {
if ((tickCount % (calibrationTickCount ~/ 20)) == 0) if ((tickCount % (calibrationTickCount ~/ 20)) == 0) {
debugPrint('Calibrating... ${(100.0 * tickCount / calibrationTickCount).floor()}%'); debugPrint('Calibrating... ${(100.0 * tickCount / calibrationTickCount).floor()}%');
}
} }
}); });
ticker.start(); ticker.start();
......
...@@ -44,8 +44,9 @@ class _RenderStatusBarPaddingSliver extends RenderSliver { ...@@ -44,8 +44,9 @@ class _RenderStatusBarPaddingSliver extends RenderSliver {
double _maxHeight; double _maxHeight;
set maxHeight(double value) { set maxHeight(double value) {
assert(maxHeight >= 0.0); assert(maxHeight >= 0.0);
if (_maxHeight == value) if (_maxHeight == value) {
return; return;
}
_maxHeight = value; _maxHeight = value;
markNeedsLayout(); markNeedsLayout();
} }
...@@ -56,8 +57,9 @@ class _RenderStatusBarPaddingSliver extends RenderSliver { ...@@ -56,8 +57,9 @@ class _RenderStatusBarPaddingSliver extends RenderSliver {
double _scrollFactor; double _scrollFactor;
set scrollFactor(double value) { set scrollFactor(double value) {
assert(scrollFactor >= 1.0); assert(scrollFactor >= 1.0);
if (_scrollFactor == value) if (_scrollFactor == value) {
return; return;
}
_scrollFactor = value; _scrollFactor = value;
markNeedsLayout(); markNeedsLayout();
} }
...@@ -390,22 +392,27 @@ class _SnappingScrollPhysics extends ClampingScrollPhysics { ...@@ -390,22 +392,27 @@ class _SnappingScrollPhysics extends ClampingScrollPhysics {
// then snap it there. Similarly if the simulation is headed down past // then snap it there. Similarly if the simulation is headed down past
// midScrollOffset but will not reach zero, then snap it to zero. // midScrollOffset but will not reach zero, then snap it to zero.
final double simulationEnd = simulation.x(double.infinity); final double simulationEnd = simulation.x(double.infinity);
if (simulationEnd >= midScrollOffset) if (simulationEnd >= midScrollOffset) {
return simulation; return simulation;
if (dragVelocity > 0.0) }
if (dragVelocity > 0.0) {
return _toMidScrollOffsetSimulation(offset, dragVelocity); return _toMidScrollOffsetSimulation(offset, dragVelocity);
if (dragVelocity < 0.0) }
if (dragVelocity < 0.0) {
return _toZeroScrollOffsetSimulation(offset, dragVelocity); return _toZeroScrollOffsetSimulation(offset, dragVelocity);
}
} else { } else {
// The user ended the drag with little or no velocity. If they // The user ended the drag with little or no velocity. If they
// didn't leave the offset above midScrollOffset, then // didn't leave the offset above midScrollOffset, then
// snap to midScrollOffset if they're more than halfway there, // snap to midScrollOffset if they're more than halfway there,
// otherwise snap to zero. // otherwise snap to zero.
final double snapThreshold = midScrollOffset / 2.0; final double snapThreshold = midScrollOffset / 2.0;
if (offset >= snapThreshold && offset < midScrollOffset) if (offset >= snapThreshold && offset < midScrollOffset) {
return _toMidScrollOffsetSimulation(offset, dragVelocity); return _toMidScrollOffsetSimulation(offset, dragVelocity);
if (offset > 0.0 && offset < snapThreshold) }
if (offset > 0.0 && offset < snapThreshold) {
return _toZeroScrollOffsetSimulation(offset, dragVelocity); return _toZeroScrollOffsetSimulation(offset, dragVelocity);
}
} }
return simulation; return simulation;
} }
...@@ -439,10 +446,11 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> { ...@@ -439,10 +446,11 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> {
} }
void _handleBackButton(double midScrollOffset) { void _handleBackButton(double midScrollOffset) {
if (_scrollController.offset >= midScrollOffset) if (_scrollController.offset >= midScrollOffset) {
_scrollController.animateTo(0.0, curve: _kScrollCurve, duration: _kScrollDuration); _scrollController.animateTo(0.0, curve: _kScrollCurve, duration: _kScrollDuration);
else } else {
Navigator.maybePop(context); Navigator.maybePop(context);
}
} }
// Only enable paging for the heading when the user has scrolled to midScrollOffset. // Only enable paging for the heading when the user has scrolled to midScrollOffset.
...@@ -478,8 +486,9 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> { ...@@ -478,8 +486,9 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> {
bool _handlePageNotification(ScrollNotification notification, PageController leader, PageController follower) { bool _handlePageNotification(ScrollNotification notification, PageController leader, PageController follower) {
if (notification.depth == 0 && notification is ScrollUpdateNotification) { if (notification.depth == 0 && notification is ScrollUpdateNotification) {
selectedIndex.value = leader.page; selectedIndex.value = leader.page;
if (follower.page != leader.page) if (follower.page != leader.page) {
follower.position.jumpToWithoutSettling(leader.position.pixels); // ignore: deprecated_member_use follower.position.jumpToWithoutSettling(leader.position.pixels); // ignore: deprecated_member_use
}
} }
return false; return false;
} }
......
...@@ -35,10 +35,12 @@ class FloatToken extends NumberToken { ...@@ -35,10 +35,12 @@ class FloatToken extends NumberToken {
static double _parse(String stringRep) { static double _parse(String stringRep) {
String toParse = stringRep; String toParse = stringRep;
if (toParse.startsWith('.')) if (toParse.startsWith('.')) {
toParse = '0$toParse'; toParse = '0$toParse';
if (toParse.endsWith('.')) }
if (toParse.endsWith('.')) {
toParse = '${toParse}0'; toParse = '${toParse}0';
}
return double.parse(toParse); return double.parse(toParse);
} }
} }
...@@ -51,8 +53,9 @@ class ResultToken extends NumberToken { ...@@ -51,8 +53,9 @@ class ResultToken extends NumberToken {
/// floating point number is guaranteed to have at least this many /// floating point number is guaranteed to have at least this many
/// decimal digits of precision. /// decimal digits of precision.
static num round(num number) { static num round(num number) {
if (number is int) if (number is int) {
return number; return number;
}
return double.parse(number.toStringAsPrecision(14)); return double.parse(number.toStringAsPrecision(14));
} }
} }
...@@ -330,10 +333,11 @@ class CalcExpression { ...@@ -330,10 +333,11 @@ class CalcExpression {
// Remove the next number token. // Remove the next number token.
final NumberToken nextNumToken = list.removeAt(0)! as NumberToken; final NumberToken nextNumToken = list.removeAt(0)! as NumberToken;
final num nextNumber = nextNumToken.number; final num nextNumber = nextNumToken.number;
if (isDivision) if (isDivision) {
currentValue /= nextNumber; currentValue /= nextNumber;
else } else {
currentValue *= nextNumber; currentValue *= nextNumber;
}
} }
return currentValue; return currentValue;
} }
......
...@@ -294,23 +294,26 @@ class _BackdropDemoState extends State<BackdropDemo> with SingleTickerProviderSt ...@@ -294,23 +294,26 @@ class _BackdropDemoState extends State<BackdropDemo> with SingleTickerProviderSt
// the user must either tap its heading or the backdrop's menu icon. // the user must either tap its heading or the backdrop's menu icon.
void _handleDragUpdate(DragUpdateDetails details) { void _handleDragUpdate(DragUpdateDetails details) {
if (_controller.isAnimating || _controller.status == AnimationStatus.completed) if (_controller.isAnimating || _controller.status == AnimationStatus.completed) {
return; return;
}
_controller.value -= details.primaryDelta! / _backdropHeight; _controller.value -= details.primaryDelta! / _backdropHeight;
} }
void _handleDragEnd(DragEndDetails details) { void _handleDragEnd(DragEndDetails details) {
if (_controller.isAnimating || _controller.status == AnimationStatus.completed) if (_controller.isAnimating || _controller.status == AnimationStatus.completed) {
return; return;
}
final double flingVelocity = details.velocity.pixelsPerSecond.dy / _backdropHeight; final double flingVelocity = details.velocity.pixelsPerSecond.dy / _backdropHeight;
if (flingVelocity < 0.0) if (flingVelocity < 0.0) {
_controller.fling(velocity: math.max(2.0, -flingVelocity)); _controller.fling(velocity: math.max(2.0, -flingVelocity));
else if (flingVelocity > 0.0) } else if (flingVelocity > 0.0) {
_controller.fling(velocity: math.min(-2.0, -flingVelocity)); _controller.fling(velocity: math.min(-2.0, -flingVelocity));
else } else {
_controller.fling(velocity: _controller.value < 0.5 ? -2.0 : 2.0); _controller.fling(velocity: _controller.value < 0.5 ? -2.0 : 2.0);
}
} }
// Stacks a BackdropPanel, which displays the selected category, on top // Stacks a BackdropPanel, which displays the selected category, on top
......
...@@ -204,12 +204,15 @@ class _BottomAppBarDemoState extends State<BottomAppBarDemo> { ...@@ -204,12 +204,15 @@ class _BottomAppBarDemoState extends State<BottomAppBarDemo> {
} }
NotchedShape? _selectNotch() { NotchedShape? _selectNotch() {
if (!_showNotch.value!) if (!_showNotch.value!) {
return null; return null;
if (_fabShape == kCircularFab) }
if (_fabShape == kCircularFab) {
return const CircularNotchedRectangle(); return const CircularNotchedRectangle();
if (_fabShape == kDiamondFab) }
if (_fabShape == kDiamondFab) {
return const _DiamondNotchedRectangle(); return const _DiamondNotchedRectangle();
}
return null; return null;
} }
} }
...@@ -452,8 +455,9 @@ class _DiamondNotchedRectangle implements NotchedShape { ...@@ -452,8 +455,9 @@ class _DiamondNotchedRectangle implements NotchedShape {
@override @override
Path getOuterPath(Rect host, Rect? guest) { Path getOuterPath(Rect host, Rect? guest) {
if (!host.overlaps(guest!)) if (!host.overlaps(guest!)) {
return Path()..addRect(host); return Path()..addRect(host);
}
assert(guest.width > 0.0); assert(guest.width > 0.0);
final Rect intersection = guest.intersect(host); final Rect intersection = guest.intersect(host);
......
...@@ -165,8 +165,9 @@ class _BottomNavigationDemoState extends State<BottomNavigationDemo> ...@@ -165,8 +165,9 @@ class _BottomNavigationDemoState extends State<BottomNavigationDemo>
@override @override
void dispose() { void dispose() {
for (final NavigationIconView view in _navigationViews) for (final NavigationIconView view in _navigationViews) {
view.controller.dispose(); view.controller.dispose();
}
super.dispose(); super.dispose();
} }
......
...@@ -97,8 +97,9 @@ class DessertDataSource extends DataTableSource { ...@@ -97,8 +97,9 @@ class DessertDataSource extends DataTableSource {
@override @override
DataRow? getRow(int index) { DataRow? getRow(int index) {
assert(index >= 0); assert(index >= 0);
if (index >= _desserts.length) if (index >= _desserts.length) {
return null; return null;
}
final Dessert dessert = _desserts[index]; final Dessert dessert = _desserts[index];
return DataRow.byIndex( return DataRow.byIndex(
index: index, index: index,
...@@ -134,8 +135,9 @@ class DessertDataSource extends DataTableSource { ...@@ -134,8 +135,9 @@ class DessertDataSource extends DataTableSource {
int get selectedRowCount => _selectedCount; int get selectedRowCount => _selectedCount;
void _selectAll(bool? checked) { void _selectAll(bool? checked) {
for (final Dessert dessert in _desserts) for (final Dessert dessert in _desserts) {
dessert.selected = checked; dessert.selected = checked;
}
_selectedCount = checked! ? _desserts.length : 0; _selectedCount = checked! ? _desserts.length : 0;
notifyListeners(); notifyListeners();
} }
......
...@@ -66,8 +66,9 @@ class _DateTimePicker extends StatelessWidget { ...@@ -66,8 +66,9 @@ class _DateTimePicker extends StatelessWidget {
firstDate: DateTime(2015, 8), firstDate: DateTime(2015, 8),
lastDate: DateTime(2101), lastDate: DateTime(2101),
); );
if (picked != null && picked != selectedDate) if (picked != null && picked != selectedDate) {
selectDate!(picked); selectDate!(picked);
}
} }
Future<void> _selectTime(BuildContext context) async { Future<void> _selectTime(BuildContext context) async {
...@@ -75,8 +76,9 @@ class _DateTimePicker extends StatelessWidget { ...@@ -75,8 +76,9 @@ class _DateTimePicker extends StatelessWidget {
context: context, context: context,
initialTime: selectedTime!, initialTime: selectedTime!,
); );
if (picked != null && picked != selectedTime) if (picked != null && picked != selectedTime) {
selectTime!(picked); selectTime!(picked);
}
} }
@override @override
......
...@@ -144,10 +144,11 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin { ...@@ -144,10 +144,11 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
onDetailsPressed: () { onDetailsPressed: () {
_showDrawerContents = !_showDrawerContents; _showDrawerContents = !_showDrawerContents;
if (_showDrawerContents) if (_showDrawerContents) {
_controller.reverse(); _controller.reverse();
else } else {
_controller.forward(); _controller.forward();
}
}, },
), ),
MediaQuery.removePadding( MediaQuery.removePadding(
......
...@@ -46,8 +46,9 @@ class DateTimeItem extends StatelessWidget { ...@@ -46,8 +46,9 @@ class DateTimeItem extends StatelessWidget {
lastDate: date.add(const Duration(days: 30)), lastDate: date.add(const Duration(days: 30)),
) )
.then((DateTime? value) { .then((DateTime? value) {
if (value != null) if (value != null) {
onChanged(DateTime(value.year, value.month, value.day, time.hour, time.minute)); onChanged(DateTime(value.year, value.month, value.day, time.hour, time.minute));
}
}); });
}, },
child: Row( child: Row(
...@@ -73,8 +74,9 @@ class DateTimeItem extends StatelessWidget { ...@@ -73,8 +74,9 @@ class DateTimeItem extends StatelessWidget {
initialTime: time, initialTime: time,
) )
.then((TimeOfDay? value) { .then((TimeOfDay? value) {
if (value != null) if (value != null) {
onChanged(DateTime(date.year, date.month, date.day, value.hour, value.minute)); onChanged(DateTime(date.year, date.month, date.day, value.hour, value.minute));
}
}); });
}, },
child: Row( child: Row(
...@@ -109,8 +111,9 @@ class FullScreenDialogDemoState extends State<FullScreenDialogDemo> { ...@@ -109,8 +111,9 @@ class FullScreenDialogDemoState extends State<FullScreenDialogDemo> {
Future<bool> _onWillPop() async { Future<bool> _onWillPop() async {
_saveNeeded = _hasLocation || _hasName || _saveNeeded; _saveNeeded = _hasLocation || _hasName || _saveNeeded;
if (!_saveNeeded) if (!_saveNeeded) {
return true; return true;
}
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final TextStyle dialogTextStyle = theme.textTheme.subtitle1!.copyWith(color: theme.textTheme.caption!.color); final TextStyle dialogTextStyle = theme.textTheme.subtitle1!.copyWith(color: theme.textTheme.caption!.color);
......
...@@ -118,8 +118,9 @@ class _GridPhotoViewerState extends State<GridPhotoViewer> with SingleTickerProv ...@@ -118,8 +118,9 @@ class _GridPhotoViewerState extends State<GridPhotoViewer> with SingleTickerProv
void _handleOnScaleEnd(ScaleEndDetails details) { void _handleOnScaleEnd(ScaleEndDetails details) {
final double magnitude = details.velocity.pixelsPerSecond.distance; final double magnitude = details.velocity.pixelsPerSecond.distance;
if (magnitude < _kMinFlingVelocity) if (magnitude < _kMinFlingVelocity) {
return; return;
}
final Offset direction = details.velocity.pixelsPerSecond / magnitude; final Offset direction = details.velocity.pixelsPerSecond / magnitude;
final double distance = (Offset.zero & context.size!).shortestSide; final double distance = (Offset.zero & context.size!).shortestSide;
_flingAnimation = _controller.drive(Tween<Offset>( _flingAnimation = _controller.drive(Tween<Offset>(
......
...@@ -222,10 +222,11 @@ class _LeaveBehindListItem extends StatelessWidget { ...@@ -222,10 +222,11 @@ class _LeaveBehindListItem extends StatelessWidget {
key: ObjectKey(item), key: ObjectKey(item),
direction: dismissDirection, direction: dismissDirection,
onDismissed: (DismissDirection direction) { onDismissed: (DismissDirection direction) {
if (direction == DismissDirection.endToStart) if (direction == DismissDirection.endToStart) {
_handleArchive(); _handleArchive();
else } else {
_handleDelete(); _handleDelete();
}
}, },
confirmDismiss: !confirmDismiss ? null : (DismissDirection dismissDirection) async { confirmDismiss: !confirmDismiss ? null : (DismissDirection dismissDirection) async {
switch(dismissDirection) { switch(dismissDirection) {
......
...@@ -224,8 +224,9 @@ class _ListDemoState extends State<ListDemo> { ...@@ -224,8 +224,9 @@ class _ListDemoState extends State<ListDemo> {
} }
Iterable<Widget> listTiles = items.map<Widget>((String item) => buildListTile(context, item)); Iterable<Widget> listTiles = items.map<Widget>((String item) => buildListTile(context, item));
if (_showDividers != null) if (_showDividers != null) {
listTiles = ListTile.divideTiles(context: context, tiles: listTiles); listTiles = ListTile.divideTiles(context: context, tiles: listTiles);
}
return Scaffold( return Scaffold(
key: scaffoldKey, key: scaffoldKey,
......
...@@ -42,16 +42,18 @@ class MenuDemoState extends State<MenuDemo> { ...@@ -42,16 +42,18 @@ class MenuDemoState extends State<MenuDemo> {
} }
void showMenuSelection(String value) { void showMenuSelection(String value) {
if (<String>[_simpleValue1, _simpleValue2, _simpleValue3].contains(value)) if (<String>[_simpleValue1, _simpleValue2, _simpleValue3].contains(value)) {
setState(() => _simpleValue = value); setState(() => _simpleValue = value);
}
showInSnackBar('You selected: $value'); showInSnackBar('You selected: $value');
} }
void showCheckedMenuSelections(String value) { void showCheckedMenuSelections(String value) {
if (_checkedValues.contains(value)) if (_checkedValues.contains(value)) {
_checkedValues.remove(value); _checkedValues.remove(value);
else } else {
_checkedValues.add(value); _checkedValues.add(value);
}
showInSnackBar('Checked $_checkedValues'); showInSnackBar('Checked $_checkedValues');
} }
......
...@@ -29,8 +29,9 @@ class OverscrollDemoState extends State<OverscrollDemo> { ...@@ -29,8 +29,9 @@ class OverscrollDemoState extends State<OverscrollDemo> {
final Completer<void> completer = Completer<void>(); final Completer<void> completer = Completer<void>();
Timer(const Duration(seconds: 3), () => completer.complete()); Timer(const Duration(seconds: 3), () => completer.complete());
return completer.future.then((_) { return completer.future.then((_) {
if (!mounted) if (!mounted) {
return; return;
}
ScaffoldMessenger.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('Refresh complete'), content: const Text('Refresh complete'),
action: SnackBarAction( action: SnackBarAction(
......
...@@ -13,8 +13,9 @@ class _PageSelector extends StatelessWidget { ...@@ -13,8 +13,9 @@ class _PageSelector extends StatelessWidget {
void _handleArrowButtonPress(BuildContext context, int delta) { void _handleArrowButtonPress(BuildContext context, int delta) {
final TabController controller = DefaultTabController.of(context)!; final TabController controller = DefaultTabController.of(context)!;
if (!controller.indexIsChanging) if (!controller.indexIsChanging) {
controller.animateTo((controller.index + delta).clamp(0, icons!.length - 1)); controller.animateTo((controller.index + delta).clamp(0, icons!.length - 1));
}
} }
@override @override
......
...@@ -33,10 +33,11 @@ class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo> with Sing ...@@ -33,10 +33,11 @@ class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo> with Sing
curve: const Interval(0.0, 0.9, curve: Curves.fastOutSlowIn), curve: const Interval(0.0, 0.9, curve: Curves.fastOutSlowIn),
reverseCurve: Curves.fastOutSlowIn, reverseCurve: Curves.fastOutSlowIn,
)..addStatusListener((AnimationStatus status) { )..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed) if (status == AnimationStatus.dismissed) {
_controller.forward(); _controller.forward();
else if (status == AnimationStatus.completed) } else if (status == AnimationStatus.completed) {
_controller.reverse(); _controller.reverse();
}
}); });
} }
......
...@@ -68,8 +68,9 @@ class ScrollableTabsDemoState extends State<ScrollableTabsDemo> with SingleTicke ...@@ -68,8 +68,9 @@ class ScrollableTabsDemoState extends State<ScrollableTabsDemo> with SingleTicke
} }
Decoration? getIndicator() { Decoration? getIndicator() {
if (!_customIndicator) if (!_customIndicator) {
return const UnderlineTabIndicator(); return const UnderlineTabIndicator();
}
switch(_demoStyle) { switch(_demoStyle) {
case TabsDemoStyle.iconsAndText: case TabsDemoStyle.iconsAndText:
......
...@@ -107,8 +107,9 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat ...@@ -107,8 +107,9 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
} }
Widget? buildFloatingActionButton(_Page page) { Widget? buildFloatingActionButton(_Page page) {
if (!page.fabDefined) if (!page.fabDefined) {
return null; return null;
}
if (_extendedButtons) { if (_extendedButtons) {
return FloatingActionButton.extended( return FloatingActionButton.extended(
......
...@@ -112,36 +112,42 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> { ...@@ -112,36 +112,42 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
String? _validateName(String? value) { String? _validateName(String? value) {
_formWasEdited = true; _formWasEdited = true;
if (value!.isEmpty) if (value!.isEmpty) {
return 'Name is required.'; return 'Name is required.';
}
final RegExp nameExp = RegExp(r'^[A-Za-z ]+$'); final RegExp nameExp = RegExp(r'^[A-Za-z ]+$');
if (!nameExp.hasMatch(value)) if (!nameExp.hasMatch(value)) {
return 'Please enter only alphabetical characters.'; return 'Please enter only alphabetical characters.';
}
return null; return null;
} }
String? _validatePhoneNumber(String? value) { String? _validatePhoneNumber(String? value) {
_formWasEdited = true; _formWasEdited = true;
final RegExp phoneExp = RegExp(r'^\(\d\d\d\) \d\d\d\-\d\d\d\d$'); final RegExp phoneExp = RegExp(r'^\(\d\d\d\) \d\d\d\-\d\d\d\d$');
if (!phoneExp.hasMatch(value!)) if (!phoneExp.hasMatch(value!)) {
return '(###) ###-#### - Enter a US phone number.'; return '(###) ###-#### - Enter a US phone number.';
}
return null; return null;
} }
String? _validatePassword(String? value) { String? _validatePassword(String? value) {
_formWasEdited = true; _formWasEdited = true;
final FormFieldState<String> passwordField = _passwordFieldKey.currentState!; final FormFieldState<String> passwordField = _passwordFieldKey.currentState!;
if (passwordField.value == null || passwordField.value!.isEmpty) if (passwordField.value == null || passwordField.value!.isEmpty) {
return 'Please enter a password.'; return 'Please enter a password.';
if (passwordField.value != value) }
if (passwordField.value != value) {
return "The passwords don't match"; return "The passwords don't match";
}
return null; return null;
} }
Future<bool> _warnUserAboutInvalidData() async { Future<bool> _warnUserAboutInvalidData() async {
final FormState? form = _formKey.currentState; final FormState? form = _formKey.currentState;
if (form == null || !_formWasEdited || form.validate()) if (form == null || !_formWasEdited || form.validate()) {
return true; return true;
}
final bool? result = await showDialog<bool>( final bool? result = await showDialog<bool>(
context: context, context: context,
...@@ -313,30 +319,35 @@ class _UsNumberTextInputFormatter extends TextInputFormatter { ...@@ -313,30 +319,35 @@ class _UsNumberTextInputFormatter extends TextInputFormatter {
final StringBuffer newText = StringBuffer(); final StringBuffer newText = StringBuffer();
if (newTextLength >= 1) { if (newTextLength >= 1) {
newText.write('('); newText.write('(');
if (newValue.selection.end >= 1) if (newValue.selection.end >= 1) {
selectionIndex++; selectionIndex++;
}
} }
if (newTextLength >= 4) { if (newTextLength >= 4) {
final String value = newValue.text.substring(0, usedSubstringIndex = 3); final String value = newValue.text.substring(0, usedSubstringIndex = 3);
newText.write('$value) '); newText.write('$value) ');
if (newValue.selection.end >= 3) if (newValue.selection.end >= 3) {
selectionIndex += 2; selectionIndex += 2;
}
} }
if (newTextLength >= 7) { if (newTextLength >= 7) {
final String value = newValue.text.substring(3, usedSubstringIndex = 6); final String value = newValue.text.substring(3, usedSubstringIndex = 6);
newText.write('$value-'); newText.write('$value-');
if (newValue.selection.end >= 6) if (newValue.selection.end >= 6) {
selectionIndex++; selectionIndex++;
}
} }
if (newTextLength >= 11) { if (newTextLength >= 11) {
final String value = newValue.text.substring(6, usedSubstringIndex = 10); final String value = newValue.text.substring(6, usedSubstringIndex = 10);
newText.write('$value '); newText.write('$value ');
if (newValue.selection.end >= 10) if (newValue.selection.end >= 10) {
selectionIndex++; selectionIndex++;
}
} }
// Dump the rest. // Dump the rest.
if (newTextLength >= usedSubstringIndex) if (newTextLength >= usedSubstringIndex) {
newText.write(newValue.text.substring(usedSubstringIndex)); newText.write(newValue.text.substring(usedSubstringIndex));
}
return TextEditingValue( return TextEditingValue(
text: newText.toString(), text: newText.toString(),
selection: TextSelection.collapsed(offset: selectionIndex), selection: TextSelection.collapsed(offset: selectionIndex),
......
...@@ -418,10 +418,11 @@ class _RecipePageState extends State<RecipePage> { ...@@ -418,10 +418,11 @@ class _RecipePageState extends State<RecipePage> {
void _toggleFavorite() { void _toggleFavorite() {
setState(() { setState(() {
if (_favoriteRecipes.contains(widget.recipe)) if (_favoriteRecipes.contains(widget.recipe)) {
_favoriteRecipes.remove(widget.recipe); _favoriteRecipes.remove(widget.recipe);
else } else {
_favoriteRecipes.add(widget.recipe); _favoriteRecipes.add(widget.recipe);
}
}); });
} }
} }
......
...@@ -533,8 +533,9 @@ class ExtraProductsNumber extends StatelessWidget { ...@@ -533,8 +533,9 @@ class ExtraProductsNumber extends StatelessWidget {
} }
Widget _buildOverflow(AppStateModel model, BuildContext context) { Widget _buildOverflow(AppStateModel model, BuildContext context) {
if (model.productsInCart.length <= 3) if (model.productsInCart.length <= 3) {
return Container(); return Container();
}
final int numOverflowProducts = _calculateOverflow(model); final int numOverflowProducts = _calculateOverflow(model);
// Maximum of 99 so padding doesn't get messy. // Maximum of 99 so padding doesn't get messy.
......
...@@ -148,8 +148,9 @@ class VideoPlayPause extends StatefulWidget { ...@@ -148,8 +148,9 @@ class VideoPlayPause extends StatefulWidget {
class _VideoPlayPauseState extends State<VideoPlayPause> { class _VideoPlayPauseState extends State<VideoPlayPause> {
_VideoPlayPauseState() { _VideoPlayPauseState() {
listener = () { listener = () {
if (mounted) if (mounted) {
setState(() { }); setState(() { });
}
}; };
} }
......
...@@ -241,16 +241,18 @@ class _BackdropState extends State<Backdrop> with SingleTickerProviderStateMixin ...@@ -241,16 +241,18 @@ class _BackdropState extends State<Backdrop> with SingleTickerProviderStateMixin
} }
void _handleDragEnd(DragEndDetails details) { void _handleDragEnd(DragEndDetails details) {
if (_controller!.isAnimating || _controller!.status == AnimationStatus.completed) if (_controller!.isAnimating || _controller!.status == AnimationStatus.completed) {
return; return;
}
final double flingVelocity = details.velocity.pixelsPerSecond.dy / _backdropHeight; final double flingVelocity = details.velocity.pixelsPerSecond.dy / _backdropHeight;
if (flingVelocity < 0.0) if (flingVelocity < 0.0) {
_controller!.fling(velocity: math.max(2.0, -flingVelocity)); _controller!.fling(velocity: math.max(2.0, -flingVelocity));
else if (flingVelocity > 0.0) } else if (flingVelocity > 0.0) {
_controller!.fling(velocity: math.min(-2.0, -flingVelocity)); _controller!.fling(velocity: math.min(-2.0, -flingVelocity));
else } else {
_controller!.fling(velocity: _controller!.value < 0.5 ? -2.0 : 2.0); _controller!.fling(velocity: _controller!.value < 0.5 ? -2.0 : 2.0);
}
} }
void _toggleFrontLayer() { void _toggleFrontLayer() {
......
...@@ -28,8 +28,9 @@ class ComponentDemoTabData { ...@@ -28,8 +28,9 @@ class ComponentDemoTabData {
@override @override
bool operator==(Object other) { bool operator==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is ComponentDemoTabData return other is ComponentDemoTabData
&& other.tabName == tabName && other.tabName == tabName
&& other.description == description && other.description == description
...@@ -67,8 +68,9 @@ class TabbedComponentDemoScaffold extends StatelessWidget { ...@@ -67,8 +68,9 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
Future<void> _showApiDocumentation(BuildContext context) async { Future<void> _showApiDocumentation(BuildContext context) async {
final String? url = demos![DefaultTabController.of(context)!.index].documentationUrl; final String? url = demos![DefaultTabController.of(context)!.index].documentationUrl;
if (url == null) if (url == null) {
return; return;
}
final Uri uri = Uri.parse(url); final Uri uri = Uri.parse(url);
if (await canLaunchUrl(uri)) { if (await canLaunchUrl(uri)) {
......
...@@ -19,10 +19,12 @@ class GalleryDemoCategory { ...@@ -19,10 +19,12 @@ class GalleryDemoCategory {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (identical(this, other)) if (identical(this, other)) {
return true; return true;
if (other.runtimeType != runtimeType) }
if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is GalleryDemoCategory return other is GalleryDemoCategory
&& other.name == name && other.name == name
&& other.icon == icon; && other.icon == icon;
......
...@@ -99,8 +99,9 @@ DropdownButton<String>( ...@@ -99,8 +99,9 @@ DropdownButton<String>(
// null indicates the user didn't select a // null indicates the user didn't select a
// new value. // new value.
setState(() { setState(() {
if (newValue != null) if (newValue != null) {
dropdownValue = newValue; dropdownValue = newValue;
}
}); });
}, },
items: <String>['One', 'Two', 'Free', 'Four'] items: <String>['One', 'Two', 'Free', 'Four']
......
...@@ -10,8 +10,9 @@ const String _kEndTag = '// END'; ...@@ -10,8 +10,9 @@ const String _kEndTag = '// END';
Map<String?, String>? _exampleCode; Map<String?, String>? _exampleCode;
Future<String?> getExampleCode(String? tag, AssetBundle bundle) async { Future<String?> getExampleCode(String? tag, AssetBundle bundle) async {
if (_exampleCode == null) if (_exampleCode == null) {
await _parseExampleCode(bundle); await _parseExampleCode(bundle);
}
return _exampleCode![tag]; return _exampleCode![tag];
} }
......
...@@ -57,8 +57,9 @@ class GalleryOptions { ...@@ -57,8 +57,9 @@ class GalleryOptions {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is GalleryOptions return other is GalleryOptions
&& other.themeMode == themeMode && other.themeMode == themeMode
&& other.textScaleFactor == textScaleFactor && other.textScaleFactor == textScaleFactor
...@@ -480,8 +481,9 @@ class GalleryOptionsPage extends StatelessWidget { ...@@ -480,8 +481,9 @@ class GalleryOptionsPage extends StatelessWidget {
List<Widget> _enabledDiagnosticItems() { List<Widget> _enabledDiagnosticItems() {
// Boolean showFoo options with a value of null: don't display // Boolean showFoo options with a value of null: don't display
// the showFoo option at all. // the showFoo option at all.
if (options == null) if (options == null) {
return const <Widget>[]; return const <Widget>[];
}
return <Widget>[ return <Widget>[
const Divider(), const Divider(),
......
...@@ -13,8 +13,9 @@ class GalleryTextScaleValue { ...@@ -13,8 +13,9 @@ class GalleryTextScaleValue {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is GalleryTextScaleValue return other is GalleryTextScaleValue
&& other.scale == scale && other.scale == scale
&& other.label == label; && other.label == label;
...@@ -47,8 +48,9 @@ class GalleryVisualDensityValue { ...@@ -47,8 +48,9 @@ class GalleryVisualDensityValue {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType) {
return false; return false;
}
return other is GalleryVisualDensityValue return other is GalleryVisualDensityValue
&& other.visualDensity == visualDensity && other.visualDensity == visualDensity
&& other.label == label; && other.label == label;
......
...@@ -95,16 +95,18 @@ class DartSyntaxHighlighter extends SyntaxHighlighter { ...@@ -95,16 +95,18 @@ class DartSyntaxHighlighter extends SyntaxHighlighter {
int currentPosition = 0; int currentPosition = 0;
for (final _HighlightSpan span in _spans) { for (final _HighlightSpan span in _spans) {
if (currentPosition != span.start) if (currentPosition != span.start) {
formattedText.add(TextSpan(text: _src!.substring(currentPosition, span.start))); formattedText.add(TextSpan(text: _src!.substring(currentPosition, span.start)));
}
formattedText.add(TextSpan(style: span.textStyle(_style), text: span.textForSpan(_src!))); formattedText.add(TextSpan(style: span.textStyle(_style), text: span.textForSpan(_src!)));
currentPosition = span.end; currentPosition = span.end;
} }
if (currentPosition != _src!.length) if (currentPosition != _src!.length) {
formattedText.add(TextSpan(text: _src!.substring(currentPosition, _src!.length))); formattedText.add(TextSpan(text: _src!.substring(currentPosition, _src!.length)));
}
return TextSpan(style: _style!.baseStyle, children: formattedText); return TextSpan(style: _style!.baseStyle, children: formattedText);
} else { } else {
...@@ -149,8 +151,9 @@ class DartSyntaxHighlighter extends SyntaxHighlighter { ...@@ -149,8 +151,9 @@ class DartSyntaxHighlighter extends SyntaxHighlighter {
endComment, endComment,
)); ));
if (eof) if (eof) {
break; break;
}
continue; continue;
} }
...@@ -260,17 +263,19 @@ class DartSyntaxHighlighter extends SyntaxHighlighter { ...@@ -260,17 +263,19 @@ class DartSyntaxHighlighter extends SyntaxHighlighter {
_HighlightType? type; _HighlightType? type;
String word = _scanner.lastMatch![0]!; String word = _scanner.lastMatch![0]!;
if (word.startsWith('_')) if (word.startsWith('_')) {
word = word.substring(1); word = word.substring(1);
}
if (_keywords.contains(word)) if (_keywords.contains(word)) {
type = _HighlightType.keyword; type = _HighlightType.keyword;
else if (_builtInTypes.contains(word)) } else if (_builtInTypes.contains(word)) {
type = _HighlightType.keyword; type = _HighlightType.keyword;
else if (_firstLetterIsUpperCase(word)) } else if (_firstLetterIsUpperCase(word)) {
type = _HighlightType.klass; type = _HighlightType.klass;
else if (word.length >= 2 && word.startsWith('k') && _firstLetterIsUpperCase(word.substring(1))) } else if (word.length >= 2 && word.startsWith('k') && _firstLetterIsUpperCase(word.substring(1))) {
type = _HighlightType.constant; type = _HighlightType.constant;
}
if (type != null) { if (type != null) {
_spans.add(_HighlightSpan( _spans.add(_HighlightSpan(
...@@ -336,21 +341,22 @@ class _HighlightSpan { ...@@ -336,21 +341,22 @@ class _HighlightSpan {
} }
TextStyle? textStyle(SyntaxHighlighterStyle? style) { TextStyle? textStyle(SyntaxHighlighterStyle? style) {
if (type == _HighlightType.number) if (type == _HighlightType.number) {
return style!.numberStyle; return style!.numberStyle;
else if (type == _HighlightType.comment) } else if (type == _HighlightType.comment) {
return style!.commentStyle; return style!.commentStyle;
else if (type == _HighlightType.keyword) } else if (type == _HighlightType.keyword) {
return style!.keywordStyle; return style!.keywordStyle;
else if (type == _HighlightType.string) } else if (type == _HighlightType.string) {
return style!.stringStyle; return style!.stringStyle;
else if (type == _HighlightType.punctuation) } else if (type == _HighlightType.punctuation) {
return style!.punctuationStyle; return style!.punctuationStyle;
else if (type == _HighlightType.klass) } else if (type == _HighlightType.klass) {
return style!.classStyle; return style!.classStyle;
else if (type == _HighlightType.constant) } else if (type == _HighlightType.constant) {
return style!.constantStyle; return style!.constantStyle;
else } else {
return style!.baseStyle; return style!.baseStyle;
}
} }
} }
...@@ -36,8 +36,9 @@ class UpdaterState extends State<Updater> { ...@@ -36,8 +36,9 @@ class UpdaterState extends State<Updater> {
final String? updateUrl = await widget.updateUrlFetcher(); final String? updateUrl = await widget.updateUrlFetcher();
final bool? wantsUpdate = await showDialog<bool>(context: context, builder: _buildDialog); final bool? wantsUpdate = await showDialog<bool>(context: context, builder: _buildDialog);
if (wantsUpdate != null && updateUrl != null && wantsUpdate) if (wantsUpdate != null && updateUrl != null && wantsUpdate) {
launchUrl(Uri.parse(updateUrl)); launchUrl(Uri.parse(updateUrl));
}
} }
Widget _buildDialog(BuildContext context) { Widget _buildDialog(BuildContext context) {
......
...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding) {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
}
// We press the "1" and the "2" buttons and check that the display // We press the "1" and the "2" buttons and check that the display
// reads "12". // reads "12".
......
...@@ -9,8 +9,9 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -9,8 +9,9 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding) {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
}
testWidgets('Flutter Gallery drawer item test', (WidgetTester tester) async { testWidgets('Flutter Gallery drawer item test', (WidgetTester tester) async {
bool hasFeedback = false; bool hasFeedback = false;
......
...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding) {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
}
testWidgets('Flutter gallery button example code displays', (WidgetTester tester) async { testWidgets('Flutter gallery button example code displays', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/6147 // Regression test for https://github.com/flutter/flutter/issues/6147
......
...@@ -45,8 +45,9 @@ class TestAssetBundle extends AssetBundle { ...@@ -45,8 +45,9 @@ class TestAssetBundle extends AssetBundle {
@override @override
Future<String> loadString(String key, { bool cache = true }) async { Future<String> loadString(String key, { bool cache = true }) async {
if (key == 'lib/gallery/example_code.dart') if (key == 'lib/gallery/example_code.dart') {
return testCodeFile; return testCodeFile;
}
return ''; return '';
} }
......
...@@ -51,10 +51,12 @@ Future<void> main() async { ...@@ -51,10 +51,12 @@ Future<void> main() async {
// Verify that _kUnsynchronizedDemos and _kSkippedDemos identify // Verify that _kUnsynchronizedDemos and _kSkippedDemos identify
// demos that actually exist. // demos that actually exist.
final List<String> allDemoTitles = kAllGalleryDemos.map((GalleryDemo demo) => demo.title).toList(); final List<String> allDemoTitles = kAllGalleryDemos.map((GalleryDemo demo) => demo.title).toList();
if (!Set<String>.from(allDemoTitles).containsAll(_kUnsynchronizedDemoTitles)) if (!Set<String>.from(allDemoTitles).containsAll(_kUnsynchronizedDemoTitles)) {
fail('Unrecognized demo titles in _kUnsynchronizedDemosTitles: $_kUnsynchronizedDemoTitles'); fail('Unrecognized demo titles in _kUnsynchronizedDemosTitles: $_kUnsynchronizedDemoTitles');
if (!Set<String>.from(allDemoTitles).containsAll(_kSkippedDemoTitles)) }
if (!Set<String>.from(allDemoTitles).containsAll(_kSkippedDemoTitles)) {
fail('Unrecognized demo names in _kSkippedDemoTitles: $_kSkippedDemoTitles'); fail('Unrecognized demo names in _kSkippedDemoTitles: $_kSkippedDemoTitles');
}
print('Starting app...'); print('Starting app...');
runApp(const GalleryApp(testMode: true)); runApp(const GalleryApp(testMode: true));
...@@ -66,8 +68,9 @@ Future<void> main() async { ...@@ -66,8 +68,9 @@ Future<void> main() async {
final Finder demoItem = find.text(demo.title); final Finder demoItem = find.text(demo.title);
print('Scrolling to "${demo.title}"...'); print('Scrolling to "${demo.title}"...');
await controller.scrollIntoView(demoItem, alignment: 0.5); await controller.scrollIntoView(demoItem, alignment: 0.5);
if (_kSkippedDemoTitles.contains(demo.title)) if (_kSkippedDemoTitles.contains(demo.title)) {
continue; continue;
}
for (int i = 0; i < 2; i += 1) { for (int i = 0; i < 2; i += 1) {
print('Tapping "${demo.title}"...'); print('Tapping "${demo.title}"...');
await controller.tap(demoItem); // Launch the demo await controller.tap(demoItem); // Launch the demo
...@@ -91,10 +94,12 @@ Future<void> main() async { ...@@ -91,10 +94,12 @@ Future<void> main() async {
final Finder backFinder = find.byElementPredicate( final Finder backFinder = find.byElementPredicate(
(Element element) { (Element element) {
final Widget widget = element.widget; final Widget widget = element.widget;
if (widget is Tooltip) if (widget is Tooltip) {
return widget.message == 'Back'; return widget.message == 'Back';
if (widget is CupertinoNavigationBarBackButton) }
if (widget is CupertinoNavigationBarBackButton) {
return true; return true;
}
return false; return false;
}, },
description: 'Material or Cupertino back button', description: 'Material or Cupertino back button',
...@@ -122,11 +127,13 @@ class _LiveWidgetController extends LiveWidgetController { ...@@ -122,11 +127,13 @@ class _LiveWidgetController extends LiveWidgetController {
/// Runs `finder` repeatedly until it finds one or more [Element]s. /// Runs `finder` repeatedly until it finds one or more [Element]s.
Future<Finder> _waitForElement(Finder finder) async { Future<Finder> _waitForElement(Finder finder) async {
if (frameSync) if (frameSync) {
await _waitUntilFrame(() => binding.transientCallbackCount == 0); await _waitUntilFrame(() => binding.transientCallbackCount == 0);
}
await _waitUntilFrame(() => finder.precache()); await _waitUntilFrame(() => finder.precache());
if (frameSync) if (frameSync) {
await _waitUntilFrame(() => binding.transientCallbackCount == 0); await _waitUntilFrame(() => binding.transientCallbackCount == 0);
}
return finder; return finder;
} }
......
...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding) {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
}
// Regression test for https://github.com/flutter/flutter/pull/5168 // Regression test for https://github.com/flutter/flutter/pull/5168
testWidgets('Pesto appbar heroics', (WidgetTester tester) async { testWidgets('Pesto appbar heroics', (WidgetTester tester) async {
......
...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -8,8 +8,9 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding) {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
}
testWidgets('Flutter Gallery app simple smoke test', (WidgetTester tester) async { testWidgets('Flutter Gallery app simple smoke test', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
......
...@@ -40,8 +40,9 @@ void reportToStringError(String name, String route, int lineNumber, List<String> ...@@ -40,8 +40,9 @@ void reportToStringError(String name, String route, int lineNumber, List<String>
void verifyToStringOutput(String name, String route, String testString) { void verifyToStringOutput(String name, String route, String testString) {
int lineNumber = 0; int lineNumber = 0;
final List<String> lines = testString.split('\n'); final List<String> lines = testString.split('\n');
if (!testString.endsWith('\n')) if (!testString.endsWith('\n')) {
reportToStringError(name, route, lines.length, lines, 'does not end with a line feed'); reportToStringError(name, route, lines.length, lines, 'does not end with a line feed');
}
for (final String line in lines) { for (final String line in lines) {
lineNumber += 1; lineNumber += 1;
if (line == '' && lineNumber != lines.length) { if (line == '' && lineNumber != lines.length) {
......
...@@ -12,8 +12,9 @@ Future<String> mockUpdateUrlFetcher() { ...@@ -12,8 +12,9 @@ Future<String> mockUpdateUrlFetcher() {
void main() { void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) if (binding is LiveTestWidgetsFlutterBinding) {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
}
// Regression test for https://github.com/flutter/flutter/pull/5168 // Regression test for https://github.com/flutter/flutter/pull/5168
testWidgets('update dialog', (WidgetTester tester) async { testWidgets('update dialog', (WidgetTester tester) async {
......
...@@ -23,8 +23,9 @@ Future<void> runDemos(List<String> demos, WidgetController controller) async { ...@@ -23,8 +23,9 @@ Future<void> runDemos(List<String> demos, WidgetController controller) async {
String? currentDemoCategory; String? currentDemoCategory;
for (final String demo in demos) { for (final String demo in demos) {
if (kSkippedDemos.contains(demo)) if (kSkippedDemos.contains(demo)) {
continue; continue;
}
final String demoName = demo.substring(0, demo.indexOf('@')); final String demoName = demo.substring(0, demo.indexOf('@'));
final String demoCategory = demo.substring(demo.indexOf('@') + 1); final String demoCategory = demo.substring(demo.indexOf('@') + 1);
......
...@@ -60,8 +60,9 @@ Future<void> saveDurationsHistogram(List<Map<String, dynamic>> events, String ou ...@@ -60,8 +60,9 @@ Future<void> saveDurationsHistogram(List<Map<String, dynamic>> events, String ou
} }
// Verify that the durations data is valid. // Verify that the durations data is valid.
if (durations.keys.isEmpty) if (durations.keys.isEmpty) {
throw 'no "Start Transition" timeline events found'; throw 'no "Start Transition" timeline events found';
}
final Map<String, int> unexpectedValueCounts = <String, int>{}; final Map<String, int> unexpectedValueCounts = <String, int>{};
durations.forEach((String routeName, List<int> values) { durations.forEach((String routeName, List<int> values) {
if (values.length != 2) { if (values.length != 2) {
...@@ -83,8 +84,9 @@ Future<void> saveDurationsHistogram(List<Map<String, dynamic>> events, String ou ...@@ -83,8 +84,9 @@ Future<void> saveDurationsHistogram(List<Map<String, dynamic>> events, String ou
while (eventIter.moveNext()) { while (eventIter.moveNext()) {
final String eventName = eventIter.current['name'] as String; final String eventName = eventIter.current['name'] as String;
if (!<String>['Start Transition', 'Frame'].contains(eventName)) if (!<String>['Start Transition', 'Frame'].contains(eventName)) {
continue; continue;
}
final String routeName = eventName == 'Start Transition' final String routeName = eventName == 'Start Transition'
? (eventIter.current['args'] as Map<String, dynamic>)['to'] as String ? (eventIter.current['args'] as Map<String, dynamic>)['to'] as String
...@@ -114,8 +116,9 @@ Future<void> runDemos(List<String> demos, FlutterDriver driver) async { ...@@ -114,8 +116,9 @@ Future<void> runDemos(List<String> demos, FlutterDriver driver) async {
String? currentDemoCategory; String? currentDemoCategory;
for (final String demo in demos) { for (final String demo in demos) {
if (kSkippedDemos.contains(demo)) if (kSkippedDemos.contains(demo)) {
continue; continue;
}
final String demoName = demo.substring(0, demo.indexOf('@')); final String demoName = demo.substring(0, demo.indexOf('@'));
final String demoCategory = demo.substring(demo.indexOf('@') + 1); final String demoCategory = demo.substring(demo.indexOf('@') + 1);
...@@ -176,8 +179,9 @@ void main([List<String> args = const <String>[]]) { ...@@ -176,8 +179,9 @@ void main([List<String> args = const <String>[]]) {
// See _handleMessages() in transitions_perf.dart. // See _handleMessages() in transitions_perf.dart.
_allDemos = List<String>.from(json.decode(await driver.requestData('demoNames')) as List<dynamic>); _allDemos = List<String>.from(json.decode(await driver.requestData('demoNames')) as List<dynamic>);
if (_allDemos.isEmpty) if (_allDemos.isEmpty) {
throw 'no demo names found'; throw 'no demo names found';
}
}); });
tearDownAll(() async { tearDownAll(() async {
......
...@@ -46,17 +46,19 @@ void diffActions(StringBuffer diffBuffer, Map<String, dynamic> originalEvent, ...@@ -46,17 +46,19 @@ void diffActions(StringBuffer diffBuffer, Map<String, dynamic> originalEvent,
final String originalActionName = final String originalActionName =
getActionName(originalActionMasked, originalEvent['action'] as int); getActionName(originalActionMasked, originalEvent['action'] as int);
if (synthesizedActionMasked != originalActionMasked) if (synthesizedActionMasked != originalActionMasked) {
diffBuffer.write( diffBuffer.write(
'action (expected: $originalActionName actual: $synthesizedActionName) '); 'action (expected: $originalActionName actual: $synthesizedActionName) ');
}
if (kPointerActions.contains(originalActionMasked) && if (kPointerActions.contains(originalActionMasked) &&
originalActionMasked == synthesizedActionMasked) { originalActionMasked == synthesizedActionMasked) {
final int originalPointer = getPointerIdx(originalEvent['action'] as int); final int originalPointer = getPointerIdx(originalEvent['action'] as int);
final int synthesizedPointer = getPointerIdx(synthesizedEvent['action'] as int); final int synthesizedPointer = getPointerIdx(synthesizedEvent['action'] as int);
if (originalPointer != synthesizedPointer) if (originalPointer != synthesizedPointer) {
diffBuffer.write( diffBuffer.write(
'pointerIdx (expected: $originalPointer actual: $synthesizedPointer action: $originalActionName '); 'pointerIdx (expected: $originalPointer actual: $synthesizedPointer action: $originalActionName ');
}
} }
} }
...@@ -122,10 +124,12 @@ void diffMaps( ...@@ -122,10 +124,12 @@ void diffMaps(
return; return;
} }
for (final String key in expected.keys) { for (final String key in expected.keys) {
if (excludeKeys.contains(key)) if (excludeKeys.contains(key)) {
continue; continue;
if (doublesApproximatelyMatch(expected[key], actual[key])) }
if (doublesApproximatelyMatch(expected[key], actual[key])) {
continue; continue;
}
if (expected[key] != actual[key]) { if (expected[key] != actual[key]) {
diffBuffer.write( diffBuffer.write(
...@@ -154,10 +158,11 @@ String getActionName(int actionMasked, int action) { ...@@ -154,10 +158,11 @@ String getActionName(int actionMasked, int action) {
'BUTTON_PRESS', 'BUTTON_PRESS',
'BUTTON_RELEASE', 'BUTTON_RELEASE',
]; ];
if (actionMasked < actionNames.length) if (actionMasked < actionNames.length) {
return '${actionNames[actionMasked]}($action)'; return '${actionNames[actionMasked]}($action)';
else } else {
return 'ACTION_$actionMasked'; return 'ACTION_$actionMasked';
}
} }
bool doublesApproximatelyMatch(dynamic a, dynamic b) => bool doublesApproximatelyMatch(dynamic a, dynamic b) =>
......
...@@ -128,16 +128,19 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -128,16 +128,19 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
await viewChannel!.invokeMethod<void>('stopTouchEvents'); await viewChannel!.invokeMethod<void>('stopTouchEvents');
if (flutterViewEvents.length != embeddedViewEvents.length) if (flutterViewEvents.length != embeddedViewEvents.length) {
return 'Synthesized ${flutterViewEvents.length} events but the embedded view received ${embeddedViewEvents.length} events'; return 'Synthesized ${flutterViewEvents.length} events but the embedded view received ${embeddedViewEvents.length} events';
}
final StringBuffer diff = StringBuffer(); final StringBuffer diff = StringBuffer();
for (int i = 0; i < flutterViewEvents.length; ++i) { for (int i = 0; i < flutterViewEvents.length; ++i) {
final String currentDiff = diffMotionEvents(flutterViewEvents[i], embeddedViewEvents[i]); final String currentDiff = diffMotionEvents(flutterViewEvents[i], embeddedViewEvents[i]);
if (currentDiff.isEmpty) if (currentDiff.isEmpty) {
continue; continue;
if (diff.isNotEmpty) }
if (diff.isNotEmpty) {
diff.write(', '); diff.write(', ');
}
diff.write(currentDiff); diff.write(currentDiff);
} }
return diff.toString(); return diff.toString();
...@@ -201,8 +204,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -201,8 +204,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
case 'onTouch': case 'onTouch':
final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>; final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>;
flutterViewEvents.insert(0, map.cast<String, dynamic>()); flutterViewEvents.insert(0, map.cast<String, dynamic>());
if (flutterViewEvents.length > kEventsBufferSize) if (flutterViewEvents.length > kEventsBufferSize) {
flutterViewEvents.removeLast(); flutterViewEvents.removeLast();
}
setState(() {}); setState(() {});
break; break;
} }
...@@ -214,8 +218,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -214,8 +218,9 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
case 'onTouch': case 'onTouch':
final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>; final Map<dynamic, dynamic> map = call.arguments as Map<dynamic, dynamic>;
embeddedViewEvents.insert(0, map.cast<String, dynamic>()); embeddedViewEvents.insert(0, map.cast<String, dynamic>());
if (embeddedViewEvents.length > kEventsBufferSize) if (embeddedViewEvents.length > kEventsBufferSize) {
embeddedViewEvents.removeLast(); embeddedViewEvents.removeLast();
}
setState(() {}); setState(() {});
break; break;
} }
...@@ -223,9 +228,10 @@ class MotionEventsBodyState extends State<MotionEventsBody> { ...@@ -223,9 +228,10 @@ class MotionEventsBodyState extends State<MotionEventsBody> {
} }
Widget buildEventTile(BuildContext context, int index) { Widget buildEventTile(BuildContext context, int index) {
if (embeddedViewEvents.length > index) if (embeddedViewEvents.length > index) {
return TouchEventDiff( return TouchEventDiff(
flutterViewEvents[index], embeddedViewEvents[index]); flutterViewEvents[index], embeddedViewEvents[index]);
}
return Text( return Text(
'Unmatched event, action: ${flutterViewEvents[index]['action']}'); 'Unmatched event, action: ${flutterViewEvents[index]['action']}');
} }
......
...@@ -31,10 +31,11 @@ class _TestAppState extends State<TestApp> { ...@@ -31,10 +31,11 @@ class _TestAppState extends State<TestApp> {
void _executeNextStep() { void _executeNextStep() {
setState(() { setState(() {
if (_step < steps.length) if (_step < steps.length) {
_result = steps[_step++](); _result = steps[_step++]();
else } else {
_result = Future<TestStepResult>.value(TestStepResult.complete); _result = Future<TestStepResult>.value(TestStepResult.complete);
}
}); });
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment