Skip to content

Clean up test infrastructure #41880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 18, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,105 changes: 540 additions & 565 deletions .cirrus.yml

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions dev/README.md
Original file line number Diff line number Diff line change
@@ -2,3 +2,6 @@ This directory contains tools and resources that the Flutter team uses
during development of the framework. The tools in this directory
should not be necessary for developing Flutter applications, though of
course they may be interesting if you are curious.

The tests in this directory are run in the `framework_tests_misc-*`
shards.
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
#!/bin/bash
set -e

# This script is only meant to be run by the Cirrus CI system, not locally.
# It must be run from the root of the Flutter repo.

function error() {
echo "$@" 1>&2
}

# This script is only meant to be run by the Cirrus CI system, not locally.
# It must be run from the root of the Flutter repo.

function accept_android_licenses() {
yes "y" | flutter doctor --android-licenses > /dev/null 2>&1
}

echo "Flutter SDK directory is: $PWD"

# Run flutter to download dependencies and precompile things, and to disable
# analytics on the bots.
echo "Downloading build dependencies and pre-compiling Flutter snapshot"
./bin/flutter config --no-analytics

# Run doctor, to print it to the log for debugging purposes.
./bin/flutter doctor -v

# Accept licenses.
echo "Accepting Android licenses."
accept_android_licenses || (error "Accepting Android licenses failed." && false)

# Run pub get in all the repo packages.
echo "Updating packages for Flutter."
./bin/flutter update-packages
148 changes: 75 additions & 73 deletions dev/bots/deploy_gallery.sh
Original file line number Diff line number Diff line change
@@ -24,88 +24,90 @@ set -x

cd "$FLUTTER_ROOT"

if [[ "$SHARD" = "deploy_gallery" ]]; then
version="$(<version)"
if [[ "$OS" == "linux" ]]; then
echo "Building Flutter Gallery $version for Android..."

# ANDROID_SDK_ROOT must be set in the env.
(
cd examples/flutter_gallery
flutter build apk --release -t lib/main_publish.dart
)
echo "Android Flutter Gallery built"
if [[ -z "$CIRRUS_PR" && "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
echo "Deploying Flutter Gallery $version to Play Store..."
set +x # Don't echo back the below.
if [ -n "$ANDROID_GALLERY_UPLOAD_KEY" ]; then
echo "$ANDROID_GALLERY_UPLOAD_KEY" | base64 --decode > /root/.android/debug.keystore
fi
set -x
(
cd examples/flutter_gallery/android
fastlane deploy_play_store
)
else
echo "Not deployed: Flutter Gallery is only deployed to the Play Store on merged and tagged dev branch commits"
version="$(<version)"
if [[ "$OS" == "linux" ]]; then
echo "Building Flutter Gallery $version for Android..."
# ANDROID_SDK_ROOT must be set in the env.
(
cd examples/flutter_gallery
flutter build apk --release -t lib/main_publish.dart
)
echo "Android Flutter Gallery built"
if [[ -z "$CIRRUS_PR" && "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
echo "Deploying Flutter Gallery $version to Play Store..."
set +x # Don't echo back the below.
if [ -n "$ANDROID_GALLERY_UPLOAD_KEY" ]; then
echo "$ANDROID_GALLERY_UPLOAD_KEY" | base64 --decode > /root/.android/debug.keystore
fi
elif [[ "$OS" == "darwin" ]]; then
echo "Building Flutter Gallery $version for iOS..."
set -x
(
cd examples/flutter_gallery
flutter build ios --release --no-codesign -t lib/main_publish.dart
cd examples/flutter_gallery/android
fastlane deploy_play_store
)
else
echo "(Not deploying; Flutter Gallery is only deployed to Play store for tagged dev branch commits.)"
fi
elif [[ "$OS" == "darwin" ]]; then
echo "Building Flutter Gallery $version for iOS..."
(
cd examples/flutter_gallery
flutter build ios --release --no-codesign -t lib/main_publish.dart

# flutter build ios will run CocoaPods script. Check generated locations.
if [[ ! -d "ios/Pods" ]]; then
echo "Error: pod install failed to setup plugins"
exit 1
fi
# flutter build ios will run CocoaPods script. Check generated locations.
if [[ ! -d "ios/Pods" ]]; then
echo "Error: pod install failed to setup plugins"
exit 1
fi

if [[ ! -d "ios/.symlinks/plugins" ]]; then
echo "Error: pod install failed to setup plugin symlinks"
exit 1
fi
if [[ ! -d "ios/.symlinks/plugins" ]]; then
echo "Error: pod install failed to setup plugin symlinks"
exit 1
fi

if [[ -d "ios/.symlinks/flutter" ]]; then
echo "Error: pod install created flutter symlink"
exit 1
fi
if [[ -d "ios/.symlinks/flutter" ]]; then
echo "Error: pod install created flutter symlink"
exit 1
fi

if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then
echo "Error: flutter_assets not assembled"
exit 1
fi
if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then
echo "Error: flutter_assets not assembled"
exit 1
fi

if [[
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" ||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" ||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data"
]]; then
echo "Error: compiled debug version of app with --release flag"
exit 1
fi
)
echo "iOS Flutter Gallery built"
if [[ -z "$CIRRUS_PR" ]]; then
if [[ "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
echo "Archiving with distribution profile and deploying to TestFlight..."
(
cd examples/flutter_gallery/ios
export DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS="-t DAV"
fastlane build_and_deploy_testflight upload:true
)
else
echo "Archiving with distribution profile..."
(
cd examples/flutter_gallery/ios
fastlane build_and_deploy_testflight
)
echo "Archive is only deployed to TestFlight on tagged dev branch commits"
fi
if [[
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" ||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" ||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data"
]]; then
echo "Error: compiled debug version of app with --release flag"
exit 1
fi
)
echo "iOS Flutter Gallery built"
if [[ -z "$CIRRUS_PR" ]]; then
if [[ "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
echo "Archiving with distribution profile and deploying to TestFlight..."
(
cd examples/flutter_gallery/ios
export DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS="-t DAV"
fastlane build_and_deploy_testflight upload:true
)
else
echo "Not deployed: Flutter Gallery is only deployed to TestFlight on merged and tagged dev branch commits"
# On iOS the signing can break as well, so we verify this regularly (not just
# on tagged dev branch commits). We can only do this post-merge, though, because
# the secrets aren't available on PRs.
echo "Testing archiving with distribution profile..."
(
cd examples/flutter_gallery/ios
fastlane build_and_deploy_testflight
)
echo "(Not deploying; Flutter Gallery is only deployed to TestFlight for tagged dev branch commits.)"
fi
else
echo "(Not archiving or deploying; Flutter Gallery archiving is only tested post-commit.)"
fi
else
echo "Doing nothing: not on the 'deploy_gallery' SHARD."
echo "Unknown OS: $OS"
echo "Aborted."
exit 1
fi
5 changes: 0 additions & 5 deletions dev/bots/run_command.dart
Original file line number Diff line number Diff line change
@@ -95,7 +95,6 @@ Future<void> runCommand(String executable, List<String> arguments, {
OutputMode outputMode = OutputMode.print,
CapturedOutput output,
bool skip = false,
bool expectFlaky = false,
bool Function(String) removeLine,
}) async {
assert((outputMode == OutputMode.capture) == (output != null),
@@ -145,10 +144,6 @@ Future<void> runCommand(String executable, List<String> arguments, {
output.stderr = _flattenToString(await savedStderr);
}

// If the test is flaky we don't care about the actual exit.
if (expectFlaky)
return;

if ((exitCode == 0) == expectNonZeroExit || (expectedExitCode != null && exitCode != expectedExitCode)) {
if (failureMessage != null) {
print(failureMessage);
1,039 changes: 521 additions & 518 deletions dev/bots/test.dart

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions dev/bots/test/bot_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

import 'common.dart';

void main() {
test('BOT variable is set on bots', () {
expect(Platform.environment['BOT'], 'true');
});
}
21 changes: 21 additions & 0 deletions dev/bots/test/sdk_directory_has_space_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

import 'package:path/path.dart' as path;

import 'common.dart';

void main() {
test('We are in a directory with a space in it', () async {
// The Flutter SDK should be in a directory with a space in it, to make sure
// our tools support that.
final String expectedName = Platform.environment['FLUTTER_SDK_PATH_WITH_SPACE'];
expect(expectedName, 'flutter sdk');
expect(expectedName, contains(' '));
final List<String> parts = path.split(Directory.current.absolute.path);
expect(parts.reversed.take(3), <String>['bots', 'dev', expectedName]);
});
}
13 changes: 11 additions & 2 deletions dev/bots/test/test_test.dart
Original file line number Diff line number Diff line change
@@ -29,7 +29,11 @@ void main() {
];
for (String version in valid_versions) {
when(file.readAsString()).thenAnswer((Invocation invocation) => Future<String>.value(version));
expect(await verifyVersion(version, file), isTrue, reason: '$version is invalid');
expect(
await verifyVersion(file),
isNull,
reason: '$version is valid but verifyVersionFile said it was bad',
);
}
});

@@ -41,10 +45,15 @@ void main() {
'1.2.3-pre',
'1.2.3-pre.1+hotfix.1',
' 1.2.3',
'1.2.3-hotfix.1',
];
for (String version in invalid_versions) {
when(file.readAsString()).thenAnswer((Invocation invocation) => Future<String>.value(version));
expect(await verifyVersion(version, file), isFalse);
expect(
await verifyVersion(file),
'The version logic generated an invalid version string: "$version".',
reason: '$version is invalid but verifyVersionFile said it was fine',
);
}
});
});
14 changes: 9 additions & 5 deletions packages/flutter_tools/dart_test.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
tags:
"no_coverage":
"create":
"integration":

# package:test configuration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does removing the tags mean we can no longer tell build_runner to build only those tags?

Is ther esome other way we can avoid building the world for each build_runner run? It takes a much longer time to build the whole flutter_tools test package than building a subshard.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this appears to currently be broken, but when it was first sharded it seemed to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If build_runner is building things it wasn't told to run, that seems like a bug in build_runner?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been fixed in build_runner. Theoretically it should be a matter of enabling it. We already use it in the engine web tests. Works pretty well there.

See also:

# https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md

# Some of our tests take an absurdly long time to run, and on some
# hosts they can take even longer due to the host suddenly being
# overloaded. For this reason, we set the test timeout to
# significantly more than it would be by default, and we never set the
# timeouts in the tests themselves.
timeout: 15m
Original file line number Diff line number Diff line change
@@ -135,7 +135,7 @@ void main() {
return _updateIdeConfig(
expectedContents: expectedContents,
);
}, timeout: const Timeout.factor(2.0));
});

testUsingContext('creates non-existent files', () async {
final Map<String, String> templateManifest = _getManifest(
@@ -155,7 +155,7 @@ void main() {
return _updateIdeConfig(
expectedContents: expectedContents,
);
}, timeout: const Timeout.factor(2.0));
});

testUsingContext('overwrites existing files with --overwrite', () async {
final Map<String, String> templateManifest = _getManifest(
@@ -181,7 +181,7 @@ void main() {
args: <String>['--overwrite'],
expectedContents: expectedContents,
);
}, timeout: const Timeout.factor(2.0));
});

testUsingContext('only adds new templates without --overwrite', () async {
final Map<String, String> templateManifest = _getManifest(
@@ -212,7 +212,7 @@ void main() {
args: <String>['--update-templates'],
expectedContents: expectedContents,
);
}, timeout: const Timeout.factor(2.0));
});

testUsingContext('update all templates with --overwrite', () async {
final Map<String, String> templateManifest = _getManifest(
@@ -239,7 +239,7 @@ void main() {
args: <String>['--update-templates', '--overwrite'],
expectedContents: expectedContents,
);
}, timeout: const Timeout.factor(2.0));
});

testUsingContext('removes deleted imls with --overwrite', () async {
final Map<String, String> templateManifest = _getManifest(
@@ -275,7 +275,7 @@ void main() {
args: <String>['--update-templates', '--overwrite'],
expectedContents: expectedContents,
);
}, timeout: const Timeout.factor(2.0));
});

testUsingContext('removes deleted imls with --overwrite, including empty parent dirs', () async {
final Map<String, String> templateManifest = _getManifest(
@@ -316,7 +316,7 @@ void main() {
args: <String>['--update-templates', '--overwrite'],
expectedContents: expectedContents,
);
}, timeout: const Timeout.factor(2.0));
});

});
}
Original file line number Diff line number Diff line change
@@ -16,9 +16,6 @@ import 'package:flutter_tools/src/runner/flutter_command.dart';
import '../../src/common.dart';
import '../../src/context.dart';

/// Test case timeout for tests involving project analysis.
const Timeout allowForSlowAnalyzeTests = Timeout.factor(5.0);

final Generator _kNoColorTerminalPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
final Map<Type, Generator> noColorTerminalOverride = <Type, Generator>{
Platform: _kNoColorTerminalPlatform,
@@ -54,7 +51,7 @@ void main() {
],
);
expect(libMain.existsSync(), isTrue);
}, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -65,7 +62,7 @@ void main() {
arguments: <String>['analyze'],
statusTextContains: <String>['No issues found!'],
);
}, timeout: allowForSlowAnalyzeTests, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -80,6 +77,7 @@ void main() {
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

// Analyze in the current directory - no arguments
testUsingContext('working directory with errors', () async {
// Break the code to produce the "The parameter 'onPressed' is required" hint
@@ -110,7 +108,7 @@ void main() {
exitMessageContains: '2 issues found.',
toolExit: true,
);
}, timeout: allowForSlowAnalyzeTests, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
...noColorTerminalOverride,
});
@@ -140,7 +138,7 @@ void main() {
exitMessageContains: '3 issues found.',
toolExit: true,
);
}, timeout: allowForSlowAnalyzeTests, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
...noColorTerminalOverride
});
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@ void main() {

expect(await command.usageValues,
containsPair(CustomDimensions.commandBuildBundleIsModule, 'true'));
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('bundle getUsage indicate that project is not a module', () async {
final String projectPath = await createProject(tempDir,
@@ -83,7 +83,7 @@ void main() {

expect(await command.usageValues,
containsPair(CustomDimensions.commandBuildBundleIsModule, 'false'));
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('bundle getUsage indicate the target platform', () async {
final String projectPath = await createProject(tempDir,
@@ -93,7 +93,7 @@ void main() {

expect(await command.usageValues,
containsPair(CustomDimensions.commandBuildBundleTargetPlatform, 'android-arm'));
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('bundle fails to build for Windows if feature is disabled', () async {
fs.file('lib/main.dart').createSync(recursive: true);

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -207,7 +207,7 @@ void main() {

expectDependenciesResolved(projectPath);
expectZeroPluginsInjected(projectPath);
}, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -220,7 +220,7 @@ void main() {

expectDependenciesResolved(projectPath);
expectZeroPluginsInjected(projectPath);
}, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -234,7 +234,7 @@ void main() {

expect(await getCommand.usageValues,
containsPair(CustomDimensions.commandPackagesNumberPlugins, '0'));
}, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -248,7 +248,7 @@ void main() {

expect(await getCommand.usageValues,
containsPair(CustomDimensions.commandPackagesProjectModule, 'false'));
}, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -262,7 +262,7 @@ void main() {

expect(await getCommand.usageValues,
containsPair(CustomDimensions.commandPackagesProjectModule, 'true'));
}, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -275,7 +275,7 @@ void main() {

expectDependenciesResolved(projectPath);
expectZeroPluginsInjected(projectPath);
}, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -288,7 +288,7 @@ void main() {

expectDependenciesResolved(projectPath);
expectModulePluginInjected(projectPath);
}, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});

@@ -309,7 +309,7 @@ void main() {

expectDependenciesResolved(exampleProjectPath);
expectPluginInjected(exampleProjectPath);
}, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{
Pub: () => const Pub(),
});
});
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('indicate that project is a plugin', () async {
final String projectPath = await createProject(tempDir,
@@ -69,7 +69,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('indicate the target platform', () async {
final String projectPath = await createProject(tempDir,
@@ -82,7 +82,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});
});

group('Gradle', () {
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('split per abi', () async {
final String projectPath = await createProject(tempDir,
@@ -65,7 +65,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('build type', () async {
final String projectPath = await createProject(tempDir,
@@ -92,7 +92,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});
});

group('Gradle', () {
@@ -223,8 +223,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('shrinking is disabled when --no-shrink is passed', () async {
final String projectPath = await createProject(tempDir,
@@ -255,8 +254,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('guides the user when the shrinker fails', () async {
final String projectPath = await createProject(tempDir,
@@ -312,8 +310,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('reports when the app isn\'t using AndroidX', () async {
final String projectPath = await createProject(tempDir,
@@ -366,8 +363,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('reports when the app is using AndroidX', () async {
final String projectPath = await createProject(tempDir,
@@ -422,8 +418,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
});
});
}

Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});

testUsingContext('build type', () async {
final String projectPath = await createProject(tempDir,
@@ -75,7 +75,7 @@ void main() {

}, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
}, timeout: allowForCreateFlutterProject);
});
});

group('Gradle', () {
@@ -212,8 +212,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('shrinking is disabled when --no-shrink is passed', () async {
final String projectPath = await createProject(
@@ -246,8 +245,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('guides the user when the shrinker fails', () async {
final String projectPath = await createProject(tempDir,
@@ -303,8 +301,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('reports when the app isn\'t using AndroidX', () async {
final String projectPath = await createProject(tempDir,
@@ -357,8 +354,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
});

testUsingContext('reports when the app is using AndroidX', () async {
final String projectPath = await createProject(tempDir,
@@ -413,8 +409,7 @@ flutter:
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
});
});
}

133 changes: 54 additions & 79 deletions packages/flutter_tools/test/integration.shard/daemon_mode_test.dart
Original file line number Diff line number Diff line change
@@ -2,13 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags(<String>['no_coverage'])
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';
@@ -21,79 +17,58 @@ import 'test_driver.dart';
import 'test_utils.dart';

void main() {
group('daemon_mode', () {
Directory tempDir;
final BasicProject _project = BasicProject();
Process process;

setUp(() async {
tempDir = createResolvedTempDirectorySync('daemon_mode_test.');
await _project.setUpIn(tempDir);
});

tearDown(() async {
tryToDelete(tempDir);
process?.kill();
});

test('device.getDevices', () async {
final String flutterBin =
fs.path.join(getFlutterRoot(), 'bin', 'flutter');

const ProcessManager processManager = LocalProcessManager();
process = await processManager.start(
<String>[flutterBin, '--show-test-device', 'daemon'],
workingDirectory: tempDir.path);

final StreamController<String> stdout =
StreamController<String>.broadcast();

transformToLines(process.stdout)
.listen((String line) => stdout.add(line));
test('device.getDevices', () async {
final Directory tempDir = createResolvedTempDirectorySync('daemon_mode_test.');

final Stream<Map<String, dynamic>> stream =
stdout.stream.where((String line) {
final Map<String, dynamic> response = parseFlutterResponse(line);
// ignore 'Starting device daemon...'
if (response == null) {
return false;
}
// TODO(devoncarew): Remove this after #25440 lands.
if (response['event'] == 'daemon.showMessage') {
return false;
}
return true;
}).map(parseFlutterResponse);

Map<String, dynamic> response = await stream.first;
expect(response['event'], 'daemon.connected');

// start listening for devices
process.stdin.writeln('[${jsonEncode(<String, dynamic>{
'id': 1,
'method': 'device.enable',
})}]');
response = await stream.first;
expect(response['id'], 1);
expect(response['error'], isNull);

// [{"event":"device.added","params":{"id":"flutter-tester","name":
// "Flutter test device","platform":"flutter-tester","emulator":false}}]
response = await stream.first;
expect(response['event'], 'device.added');

// get the list of all devices
process.stdin.writeln('[${jsonEncode(<String, dynamic>{
'id': 2,
'method': 'device.getDevices',
})}]');
response = await stream.first;
expect(response['id'], 2);
expect(response['error'], isNull);

final dynamic result = response['result'];
expect(result, isList);
expect(result, isNotEmpty);
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // This test uses the `flutter` tool, which could be blocked behind the startup lock for a long time.
final BasicProject _project = BasicProject();
await _project.setUpIn(tempDir);

final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');

const ProcessManager processManager = LocalProcessManager();
final Process process = await processManager.start(
<String>[flutterBin, '--show-test-device', 'daemon'],
workingDirectory: tempDir.path,
);

final StreamController<String> stdout = StreamController<String>.broadcast();
transformToLines(process.stdout).listen((String line) => stdout.add(line));
final Stream<Map<String, dynamic>> stream = stdout
.stream
.map<Map<String, dynamic>>(parseFlutterResponse)
.where((Map<String, dynamic> value) => value != null);

Map<String, dynamic> response = await stream.first;
expect(response['event'], 'daemon.connected');

// start listening for devices
process.stdin.writeln('[${jsonEncode(<String, dynamic>{
'id': 1,
'method': 'device.enable',
})}]');
response = await stream.first;
expect(response['id'], 1);
expect(response['error'], isNull);

// [{"event":"device.added","params":{"id":"flutter-tester","name":
// "Flutter test device","platform":"flutter-tester","emulator":false}}]
response = await stream.first;
expect(response['event'], 'device.added');

// get the list of all devices
process.stdin.writeln('[${jsonEncode(<String, dynamic>{
'id': 2,
'method': 'device.getDevices',
})}]');
response = await stream.first;
expect(response['id'], 2);
expect(response['error'], isNull);

final dynamic result = response['result'];
expect(result, isList);
expect(result, isNotEmpty);

tryToDelete(tempDir);
process.kill();
});
}
Original file line number Diff line number Diff line change
@@ -2,11 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags(<String>['no_coverage'])
import 'dart:io';

import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';

@@ -16,43 +13,37 @@ import 'test_driver.dart';
import 'test_utils.dart';

void main() {
group('debugger', () {
Directory tempDir;
test('can step over statements', () async {
final Directory tempDir = createResolvedTempDirectorySync('debugger_stepping_test.');

final SteppingProject _project = SteppingProject();
FlutterRunTestDriver _flutter;

setUp(() async {
tempDir = createResolvedTempDirectorySync('debugger_stepping_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});

tearDown(() async {
await _flutter.stop();
tryToDelete(tempDir);
});

test('can step over statements', () async {
await _flutter.run(withDebugger: true, startPaused: true);
await _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine);
await _flutter.resume();
await _flutter.waitForPause(); // Now we should be on the breakpoint.

expect((await _flutter.getSourceLocation()).line, equals(_project.breakpointLine));

// Issue 5 steps, ensuring that we end up on the annotated lines each time.
for (int i = 1; i <= _project.numberOfSteps; i += 1) {
await _flutter.stepOverOrOverAsyncSuspension();
final SourcePosition location = await _flutter.getSourceLocation();
final int actualLine = location.line;

// Get the line we're expected to stop at by searching for the comment
// within the source code.
final int expectedLine = _project.lineForStep(i);

expect(actualLine, equals(expectedLine),
reason: 'After $i steps, debugger should stop at $expectedLine but stopped at $actualLine');
}
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
await _project.setUpIn(tempDir);

final FlutterRunTestDriver _flutter = FlutterRunTestDriver(tempDir);

await _flutter.run(withDebugger: true, startPaused: true);
await _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine);
await _flutter.resume();
await _flutter.waitForPause(); // Now we should be on the breakpoint.

expect((await _flutter.getSourceLocation()).line, equals(_project.breakpointLine));

// Issue 5 steps, ensuring that we end up on the annotated lines each time.
for (int i = 1; i <= _project.numberOfSteps; i += 1) {
await _flutter.stepOverOrOverAsyncSuspension();
final SourcePosition location = await _flutter.getSourceLocation();
final int actualLine = location.line;

// Get the line we're expected to stop at by searching for the comment
// within the source code.
final int expectedLine = _project.lineForStep(i);

expect(actualLine, equals(expectedLine),
reason: 'After $i steps, debugger should stop at $expectedLine but stopped at $actualLine'
);
}

await _flutter.stop();
tryToDelete(tempDir);
});
}
Original file line number Diff line number Diff line change
@@ -2,12 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags(<String>['no_coverage'])
import 'dart:async';
import 'dart:io';

import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';
@@ -20,120 +16,133 @@ import 'test_data/tests_project.dart';
import 'test_driver.dart';
import 'test_utils.dart';

void main() {
group('flutter run expression evaluation', () {
Directory tempDir;
final BasicProject _project = BasicProject();
FlutterRunTestDriver _flutter;

setUp(() async {
tempDir = createResolvedTempDirectorySync('run_expression_eval_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});

tearDown(() async {
await _flutter.stop();
tryToDelete(tempDir);
});

Future<void> breakInBuildMethod(FlutterTestDriver flutter) async {
await _flutter.breakAt(
_project.buildMethodBreakpointUri,
_project.buildMethodBreakpointLine,
);
}

Future<void> breakInTopLevelFunction(FlutterTestDriver flutter) async {
await _flutter.breakAt(
_project.topLevelFunctionBreakpointUri,
_project.topLevelFunctionBreakpointLine,
);
}

test('can evaluate trivial expressions in top level function', () async {
await _flutter.run(withDebugger: true);
await breakInTopLevelFunction(_flutter);
await evaluateTrivialExpressions(_flutter);
});

test('can evaluate trivial expressions in build method', () async {
await _flutter.run(withDebugger: true);
await breakInBuildMethod(_flutter);
await evaluateTrivialExpressions(_flutter);
});

test('can evaluate complex expressions in top level function', () async {
await _flutter.run(withDebugger: true);
await breakInTopLevelFunction(_flutter);
await evaluateComplexExpressions(_flutter);
});

test('can evaluate complex expressions in build method', () async {
await _flutter.run(withDebugger: true);
await breakInBuildMethod(_flutter);
await evaluateComplexExpressions(_flutter);
});

test('can evaluate expressions returning complex objects in top level function', () async {
await _flutter.run(withDebugger: true);
await breakInTopLevelFunction(_flutter);
await evaluateComplexReturningExpressions(_flutter);
});

test('can evaluate expressions returning complex objects in build method', () async {
await _flutter.run(withDebugger: true);
await breakInBuildMethod(_flutter);
await evaluateComplexReturningExpressions(_flutter);
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.

group('flutter test expression evaluation', () {
Directory tempDir;
final TestsProject _project = TestsProject();
FlutterTestTestDriver _flutter;

setUp(() async {
tempDir = createResolvedTempDirectorySync('test_expression_eval_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterTestTestDriver(tempDir);
});

tearDown(() async {
await _flutter.quit();
tryToDelete(tempDir);
});

test('can evaluate trivial expressions in a test', () async {
await _flutter.test(
withDebugger: true,
beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
);
await _flutter.waitForPause();
await evaluateTrivialExpressions(_flutter);
await _flutter.resume();
});

test('can evaluate complex expressions in a test', () async {
await _flutter.test(
withDebugger: true,
beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
);
await _flutter.waitForPause();
await evaluateComplexExpressions(_flutter);
await _flutter.resume();
});

test('can evaluate expressions returning complex objects in a test', () async {
await _flutter.test(
withDebugger: true,
beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
);
await _flutter.waitForPause();
await evaluateComplexReturningExpressions(_flutter);
await _flutter.resume();
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
void batch1() {
final BasicProject _project = BasicProject();
Directory tempDir;
FlutterRunTestDriver _flutter;

Future<void> initProject() async {
tempDir = createResolvedTempDirectorySync('run_expression_eval_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
}

Future<void> cleanProject() async {
await _flutter.stop();
tryToDelete(tempDir);
}

Future<void> breakInBuildMethod(FlutterTestDriver flutter) async {
await _flutter.breakAt(
_project.buildMethodBreakpointUri,
_project.buildMethodBreakpointLine,
);
}

Future<void> breakInTopLevelFunction(FlutterTestDriver flutter) async {
await _flutter.breakAt(
_project.topLevelFunctionBreakpointUri,
_project.topLevelFunctionBreakpointLine,
);
}

test('flutter run expression evaluation - can evaluate trivial expressions in top level function', () async {
await initProject();
await _flutter.run(withDebugger: true);
await breakInTopLevelFunction(_flutter);
await evaluateTrivialExpressions(_flutter);
await cleanProject();
});

test('flutter run expression evaluation - can evaluate trivial expressions in build method', () async {
await initProject();
await _flutter.run(withDebugger: true);
await breakInBuildMethod(_flutter);
await evaluateTrivialExpressions(_flutter);
await cleanProject();
});

test('flutter run expression evaluation - can evaluate complex expressions in top level function', () async {
await initProject();
await _flutter.run(withDebugger: true);
await breakInTopLevelFunction(_flutter);
await evaluateComplexExpressions(_flutter);
await cleanProject();
});

test('flutter run expression evaluation - can evaluate complex expressions in build method', () async {
await initProject();
await _flutter.run(withDebugger: true);
await breakInBuildMethod(_flutter);
await evaluateComplexExpressions(_flutter);
await cleanProject();
});

test('flutter run expression evaluation - can evaluate expressions returning complex objects in top level function', () async {
await initProject();
await _flutter.run(withDebugger: true);
await breakInTopLevelFunction(_flutter);
await evaluateComplexReturningExpressions(_flutter);
await cleanProject();
});

test('flutter run expression evaluation - can evaluate expressions returning complex objects in build method', () async {
await initProject();
await _flutter.run(withDebugger: true);
await breakInBuildMethod(_flutter);
await evaluateComplexReturningExpressions(_flutter);
await cleanProject();
});
}

void batch2() {
final TestsProject _project = TestsProject();
Directory tempDir;
FlutterTestTestDriver _flutter;

Future<void> initProject() async {
tempDir = createResolvedTempDirectorySync('test_expression_eval_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterTestTestDriver(tempDir);
}

Future<void> cleanProject() async {
await _flutter?.quit();
tryToDelete(tempDir);
}

test('flutter test expression evaluation - can evaluate trivial expressions in a test', () async {
await initProject();
await _flutter.test(
withDebugger: true,
beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
);
await _flutter.waitForPause();
await evaluateTrivialExpressions(_flutter);
await cleanProject();
});

test('flutter test expression evaluation - can evaluate complex expressions in a test', () async {
await initProject();
await _flutter.test(
withDebugger: true,
beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
);
await _flutter.waitForPause();
await evaluateComplexExpressions(_flutter);
await cleanProject();
});

test('flutter test expression evaluation - can evaluate expressions returning complex objects in a test', () async {
await initProject();
await _flutter.test(
withDebugger: true,
beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
);
await _flutter.waitForPause();
await evaluateComplexReturningExpressions(_flutter);
await cleanProject();
});
}

Future<void> evaluateTrivialExpressions(FlutterTestDriver flutter) async {
@@ -164,3 +173,8 @@ Future<void> evaluateComplexReturningExpressions(FlutterTestDriver flutter) asyn
final InstanceRef res = await flutter.evaluate(resp.id, r'"$year-$month-$day"');
expect(res.valueAsString, equals('${now.year}-${now.month}-${now.day}'));
}

void main() {
batch1();
batch2();
}
Original file line number Diff line number Diff line change
@@ -2,11 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags(<String>['no_coverage'])
import 'dart:io';

import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';

@@ -33,35 +30,36 @@ void main() {
tryToDelete(tempDir);
});

group('attached process', () {
test('writes pid-file', () async {
final File pidFile = tempDir.childFile('test.pid');
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(
_flutterRun.vmServicePort,
pidFile: pidFile,
);
expect(pidFile.existsSync(), isTrue);
});
test('can hot reload', () async {
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.hotReload();
});
test('can detach, reattach, hot reload', () async {
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.detach();
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.hotReload();
});
test('killing process behaves the same as detach ', () async {
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.quit();
_flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH-2');
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.hotReload();
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
test('writes pid-file', () async {
final File pidFile = tempDir.childFile('test.pid');
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(
_flutterRun.vmServicePort,
pidFile: pidFile,
);
expect(pidFile.existsSync(), isTrue);
});

test('can hot reload', () async {
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.hotReload();
});

test('can detach, reattach, hot reload', () async {
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.detach();
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.hotReload();
});

test('killing process behaves the same as detach ', () async {
await _flutterRun.run(withDebugger: true);
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.quit();
_flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH-2');
await _flutterAttach.attach(_flutterRun.vmServicePort);
await _flutterAttach.hotReload();
});
}
89 changes: 41 additions & 48 deletions packages/flutter_tools/test/integration.shard/flutter_run_test.dart
Original file line number Diff line number Diff line change
@@ -2,11 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags(<String>['no_coverage'])
import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
@@ -18,47 +13,45 @@ import 'test_driver.dart';
import 'test_utils.dart';

void main() {
group('flutter_run', () {
Directory tempDir;
final BasicProject _project = BasicProject();
FlutterRunTestDriver _flutter;

setUp(() async {
tempDir = createResolvedTempDirectorySync('run_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});

tearDown(() async {
await _flutter.stop();
tryToDelete(tempDir);
});

test('reports an error if an invalid device is supplied', () async {
// This test forces flutter to check for all possible devices to catch issues
// like https://github.com/flutter/flutter/issues/21418 which were skipped
// over because other integration tests run using flutter-tester which short-cuts
// some of the checks for devices.
final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');

const ProcessManager _processManager = LocalProcessManager();
final ProcessResult _proc = await _processManager.run(
<String>[flutterBin, 'run', '-d', 'invalid-device-id'],
workingDirectory: tempDir.path,
);

expect(_proc.stdout, isNot(contains('flutter has exited unexpectedly')));
expect(_proc.stderr, isNot(contains('flutter has exited unexpectedly')));
if (!_proc.stderr.toString().contains('Unable to locate a development')
&& !_proc.stdout.toString().contains('No devices found with name or id matching')) {
fail("'flutter run -d invalid-device-id' did not produce the expected error");
}
});

test('writes pid-file', () async {
final File pidFile = tempDir.childFile('test.pid');
await _flutter.run(pidFile: pidFile);
expect(pidFile.existsSync(), isTrue);
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
Directory tempDir;
final BasicProject _project = BasicProject();
FlutterRunTestDriver _flutter;

setUp(() async {
tempDir = createResolvedTempDirectorySync('run_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});

tearDown(() async {
await _flutter.stop();
tryToDelete(tempDir);
});

test('flutter run reports an error if an invalid device is supplied', () async {
// This test forces flutter to check for all possible devices to catch issues
// like https://github.com/flutter/flutter/issues/21418 which were skipped
// over because other integration tests run using flutter-tester which short-cuts
// some of the checks for devices.
final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');

const ProcessManager _processManager = LocalProcessManager();
final ProcessResult _proc = await _processManager.run(
<String>[flutterBin, 'run', '-d', 'invalid-device-id'],
workingDirectory: tempDir.path,
);

expect(_proc.stdout, isNot(contains('flutter has exited unexpectedly')));
expect(_proc.stderr, isNot(contains('flutter has exited unexpectedly')));
if (!_proc.stderr.toString().contains('Unable to locate a development')
&& !_proc.stdout.toString().contains('No devices found with name or id matching')) {
fail("'flutter run -d invalid-device-id' did not produce the expected error");
}
});

test('flutter run writes pid-file', () async {
final File pidFile = tempDir.childFile('test.pid');
await _flutter.run(pidFile: pidFile);
expect(pidFile.existsSync(), isTrue);
});
}
259 changes: 126 additions & 133 deletions packages/flutter_tools/test/integration.shard/hot_reload_test.dart
Original file line number Diff line number Diff line change
@@ -2,11 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags(<String>['no_coverage'])
import 'dart:async';

import 'package:file/file.dart';
@@ -20,139 +15,137 @@ import 'test_driver.dart';
import 'test_utils.dart';

void main() {
group('hot reload tests', () {
Directory tempDir;
final HotReloadProject _project = HotReloadProject();
FlutterRunTestDriver _flutter;
Directory tempDir;
final HotReloadProject _project = HotReloadProject();
FlutterRunTestDriver _flutter;

setUp(() async {
tempDir = createResolvedTempDirectorySync('hot_reload_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});
setUp(() async {
tempDir = createResolvedTempDirectorySync('hot_reload_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});

tearDown(() async {
await _flutter?.stop();
tryToDelete(tempDir);
});
tearDown(() async {
await _flutter?.stop();
tryToDelete(tempDir);
});

test('hot reload works without error', () async {
await _flutter.run();
test('hot reload works without error', () async {
await _flutter.run();
await _flutter.hotReload();
});

test('newly added code executes during hot reload', () async {
await _flutter.run();
_project.uncommentHotReloadPrint();
final StringBuffer stdout = StringBuffer();
final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
try {
await _flutter.hotReload();
});
expect(stdout.toString(), contains('(((((RELOAD WORKED)))))'));
} finally {
await subscription.cancel();
}
});

test('newly added code executes during hot reload', () async {
await _flutter.run();
_project.uncommentHotReloadPrint();
final StringBuffer stdout = StringBuffer();
final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
try {
await _flutter.hotReload();
expect(stdout.toString(), contains('(((((RELOAD WORKED)))))'));
} finally {
await subscription.cancel();
}
});
test('hot restart works without error', () async {
await _flutter.run();
await _flutter.hotRestart();
});

test('hot restart works without error', () async {
await _flutter.run();
await _flutter.hotRestart();
});
test('breakpoints are hit after hot reload', () async {
Isolate isolate;
await _flutter.run(withDebugger: true, startPaused: true);
final Completer<void> sawTick1 = Completer<void>();
final Completer<void> sawTick3 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage = Completer<void>();
final StreamSubscription<String> subscription = _flutter.stdout.listen(
(String line) {
if (line.contains('((((TICK 1))))')) {
expect(sawTick1.isCompleted, isFalse);
sawTick1.complete();
}
if (line.contains('((((TICK 3))))')) {
expect(sawTick3.isCompleted, isFalse);
sawTick3.complete();
}
if (line.contains('The application is paused in the debugger on a breakpoint.')) {
expect(sawDebuggerPausedMessage.isCompleted, isFalse);
sawDebuggerPausedMessage.complete();
}
},
);
await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts
unawaited(sawTick1.future.timeout(
const Duration(seconds: 5),
onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); },
));
await sawTick1.future; // after this, app is in steady state
await _flutter.addBreakpoint(
_project.scheduledBreakpointUri,
_project.scheduledBreakpointLine,
);
await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint
isolate = await _flutter.waitForPause();
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
await _flutter.resume();
await _flutter.addBreakpoint(
_project.buildBreakpointUri,
_project.buildBreakpointLine,
);
bool reloaded = false;
final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
await sawTick3.future; // this should happen before it pauses
isolate = await _flutter.waitForPause();
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
await sawDebuggerPausedMessage.future;
expect(reloaded, isFalse);
await _flutter.resume();
await reloadFuture;
expect(reloaded, isTrue);
reloaded = false;
await subscription.cancel();
});

test('breakpoints are hit after hot reload', () async {
Isolate isolate;
await _flutter.run(withDebugger: true, startPaused: true);
final Completer<void> sawTick1 = Completer<void>();
final Completer<void> sawTick3 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage = Completer<void>();
final StreamSubscription<String> subscription = _flutter.stdout.listen(
(String line) {
if (line.contains('((((TICK 1))))')) {
expect(sawTick1.isCompleted, isFalse);
sawTick1.complete();
}
if (line.contains('((((TICK 3))))')) {
expect(sawTick3.isCompleted, isFalse);
sawTick3.complete();
}
if (line.contains('The application is paused in the debugger on a breakpoint.')) {
expect(sawDebuggerPausedMessage.isCompleted, isFalse);
sawDebuggerPausedMessage.complete();
}
},
);
await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts
unawaited(sawTick1.future.timeout(
const Duration(seconds: 5),
onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); },
));
await sawTick1.future; // after this, app is in steady state
await _flutter.addBreakpoint(
_project.scheduledBreakpointUri,
_project.scheduledBreakpointLine,
);
await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint
isolate = await _flutter.waitForPause();
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
await _flutter.resume();
await _flutter.addBreakpoint(
_project.buildBreakpointUri,
_project.buildBreakpointLine,
);
bool reloaded = false;
final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
await sawTick3.future; // this should happen before it pauses
isolate = await _flutter.waitForPause();
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
await sawDebuggerPausedMessage.future;
expect(reloaded, isFalse);
await _flutter.resume();
await reloadFuture;
expect(reloaded, isTrue);
reloaded = false;
await subscription.cancel();
});

test('hot reload doesn\'t reassemble if paused', () async {
await _flutter.run(withDebugger: true);
final Completer<void> sawTick2 = Completer<void>();
final Completer<void> sawTick3 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage1 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage2 = Completer<void>();
final StreamSubscription<String> subscription = _flutter.stdout.listen(
(String line) {
if (line.contains('((((TICK 2))))')) {
expect(sawTick2.isCompleted, isFalse);
sawTick2.complete();
}
if (line.contains('The application is paused in the debugger on a breakpoint.')) {
expect(sawDebuggerPausedMessage1.isCompleted, isFalse);
sawDebuggerPausedMessage1.complete();
}
if (line.contains('The application is paused in the debugger on a breakpoint; interface might not update.')) {
expect(sawDebuggerPausedMessage2.isCompleted, isFalse);
sawDebuggerPausedMessage2.complete();
}
},
);
await _flutter.addBreakpoint(
_project.buildBreakpointUri,
_project.buildBreakpointLine,
);
bool reloaded = false;
final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
await sawTick2.future; // this should happen before it pauses
final Isolate isolate = await _flutter.waitForPause();
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
expect(reloaded, isFalse);
await sawDebuggerPausedMessage1.future; // this is the one where it say "uh, you broke into the debugger while reloading"
await reloadFuture; // this is the one where it times out because you're in the debugger
expect(reloaded, isTrue);
await _flutter.hotReload(); // now we're already paused
expect(sawTick3.isCompleted, isFalse);
await sawDebuggerPausedMessage2.future; // so we just get told that nothing is going to happen
await _flutter.resume();
await subscription.cancel();
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
test('hot reload doesn\'t reassemble if paused', () async {
await _flutter.run(withDebugger: true);
final Completer<void> sawTick2 = Completer<void>();
final Completer<void> sawTick3 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage1 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage2 = Completer<void>();
final StreamSubscription<String> subscription = _flutter.stdout.listen(
(String line) {
if (line.contains('((((TICK 2))))')) {
expect(sawTick2.isCompleted, isFalse);
sawTick2.complete();
}
if (line.contains('The application is paused in the debugger on a breakpoint.')) {
expect(sawDebuggerPausedMessage1.isCompleted, isFalse);
sawDebuggerPausedMessage1.complete();
}
if (line.contains('The application is paused in the debugger on a breakpoint; interface might not update.')) {
expect(sawDebuggerPausedMessage2.isCompleted, isFalse);
sawDebuggerPausedMessage2.complete();
}
},
);
await _flutter.addBreakpoint(
_project.buildBreakpointUri,
_project.buildBreakpointLine,
);
bool reloaded = false;
final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
await sawTick2.future; // this should happen before it pauses
final Isolate isolate = await _flutter.waitForPause();
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
expect(reloaded, isFalse);
await sawDebuggerPausedMessage1.future; // this is the one where it say "uh, you broke into the debugger while reloading"
await reloadFuture; // this is the one where it times out because you're in the debugger
expect(reloaded, isTrue);
await _flutter.hotReload(); // now we're already paused
expect(sawTick3.isCompleted, isFalse);
await sawDebuggerPausedMessage2.future; // so we just get told that nothing is going to happen
await _flutter.resume();
await subscription.cancel();
});
}
59 changes: 26 additions & 33 deletions packages/flutter_tools/test/integration.shard/lifetime_test.dart
Original file line number Diff line number Diff line change
@@ -2,11 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags(<String>['no_coverage'])
import 'dart:async';

import 'package:file/file.dart';
@@ -23,32 +18,30 @@ import 'test_utils.dart';
const Duration requiredLifespan = Duration(seconds: 5);

void main() {
group('flutter run', () {
final BasicProject _project = BasicProject();
FlutterRunTestDriver _flutter;
Directory tempDir;

setUp(() async {
tempDir = createResolvedTempDirectorySync('lifetime_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});

tearDown(() async {
await _flutter.stop();
tryToDelete(tempDir);
});

test('does not terminate when a debugger is attached', () async {
await _flutter.run(withDebugger: true);
await Future<void>.delayed(requiredLifespan);
expect(_flutter.hasExited, equals(false));
});

test('does not terminate when a debugger is attached and pause-on-exceptions', () async {
await _flutter.run(withDebugger: true, pauseOnExceptions: true);
await Future<void>.delayed(requiredLifespan);
expect(_flutter.hasExited, equals(false));
});
}, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
final BasicProject _project = BasicProject();
FlutterRunTestDriver _flutter;
Directory tempDir;

setUp(() async {
tempDir = createResolvedTempDirectorySync('lifetime_test.');
await _project.setUpIn(tempDir);
_flutter = FlutterRunTestDriver(tempDir);
});

tearDown(() async {
await _flutter.stop();
tryToDelete(tempDir);
});

test('flutter run does not terminate when a debugger is attached', () async {
await _flutter.run(withDebugger: true);
await Future<void>.delayed(requiredLifespan);
expect(_flutter.hasExited, equals(false));
});

test('fluter run does not terminate when a debugger is attached and pause-on-exceptions', () async {
await _flutter.run(withDebugger: true, pauseOnExceptions: true);
await Future<void>.delayed(requiredLifespan);
expect(_flutter.hasExited, equals(false));
});
}
7 changes: 0 additions & 7 deletions packages/flutter_tools/test/src/common.dart
Original file line number Diff line number Diff line change
@@ -127,13 +127,6 @@ Future<String> createProject(Directory temp, { List<String> arguments }) async {
return projectPath;
}

/// Test case timeout for tests involving remote calls to `pub get` or similar.
const Timeout allowForRemotePubInvocation = Timeout.factor(10.0);

/// Test case timeout for tests involving creating a Flutter project with
/// `--no-pub`. Use [allowForRemotePubInvocation] when creation involves `pub`.
const Timeout allowForCreateFlutterProject = Timeout.factor(3.0);

Future<void> expectToolExitLater(Future<dynamic> future, Matcher messageMatcher) async {
try {
await future;
4 changes: 1 addition & 3 deletions packages/flutter_tools/test/src/context.dart
Original file line number Diff line number Diff line change
@@ -48,7 +48,6 @@ typedef ContextInitializer = void Function(AppContext testContext);
void testUsingContext(
String description,
dynamic testMethod(), {
Timeout timeout,
Map<Type, Generator> overrides = const <Type, Generator>{},
bool initializeFlutterRoot = true,
String testOn,
@@ -137,8 +136,7 @@ void testUsingContext(
},
);
});
}, timeout: timeout ?? const Timeout(Duration(seconds: 60)),
testOn: testOn, skip: skip);
}, testOn: testOn, skip: skip);
}

void _printBufferedErrors(AppContext testContext) {