Unverified Commit 958f7c5b authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] ensure generated entrypoint matches test and web entrypoint...

[flutter_tools] ensure generated entrypoint matches test and web entrypoint language version (#59291)

Ensure that the language version of the test/web generated entrypoint matches the language version of the test file to run, or the overall package language version if no annotation is provided.
parent 584fd5f9
......@@ -544,7 +544,7 @@ Future<void> _runAddToAppLifeCycleTests() async {
Future<void> _runFrameworkTests() async {
final bq.BigqueryApi bigqueryApi = await _getBigqueryApi();
final List<String> nullSafetyOptions = <String>['--enable-experiment=non-nullable', '--no-sound-null-safety'];
final List<String> nullSafetyOptions = <String>['--enable-experiment=non-nullable'];
final List<String> trackWidgetCreationAlternatives = <String>['--track-widget-creation', '--no-track-widget-creation'];
Future<void> runWidgets() async {
......
......@@ -2,11 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
// @dart=2.8
import 'package:flutter_test/flutter_test.dart';
String? x;
void main() {
testWidgets('trivial', (WidgetTester tester) async {
expect(true, true);
......
......@@ -23,6 +23,7 @@ import '../base/utils.dart';
import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../dart/language_version.dart';
import '../dart/pub.dart';
import '../devfs.dart';
import '../device.dart';
......@@ -581,6 +582,10 @@ class _ResidentWebRunner extends ResidentWebRunner {
}
final String entrypoint = <String>[
determineLanguageVersion(
globals.fs.file(mainUri),
packageConfig[flutterProject.manifest.appName],
),
'// Flutter web bootstrap script for $importedEntrypoint.',
'',
"import 'dart:ui' as ui;",
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/file.dart';
import 'package:package_config/package_config.dart';
final RegExp _languageVersion = RegExp(r'\/\/\s*@dart');
final RegExp _declarationEnd = RegExp('(import)|(library)|(part)');
const String _blockCommentStart = '/*';
const String _blockCommentEnd = '*/';
/// Attempts to read the language version of a dart [file], returning
/// the entire comment.
///
/// If this is not present, falls back to the language version defined in
/// [package]. If [package] is not provided and there is no
/// language version header, returns `null`. This does not specifically check
/// for language declarations other than library, part, or import.
///
/// The specification for the language version tag is defined at:
/// https://github.com/dart-lang/language/blob/master/accepted/future-releases/language-versioning/feature-specification.md#individual-library-language-version-override
String determineLanguageVersion(File file, Package package) {
int blockCommentDepth = 0;
for (final String line in file.readAsLinesSync()) {
final String trimmedLine = line.trim();
if (trimmedLine.isEmpty) {
continue;
}
// Check for the start or end of a block comment. Within a block
// comment, all language version declarations are ignored. Block
// comments can be nested, and the start or end may occur on
// the same line. This does not handle the case of invalid
// block comment combinations like `*/ /*` since that will cause
// a compilation error anyway.
bool sawBlockComment = false;
final int startMatches = _blockCommentStart.allMatches(trimmedLine).length;
final int endMatches = _blockCommentEnd.allMatches(trimmedLine).length;
if (startMatches > 0) {
blockCommentDepth += startMatches;
sawBlockComment = true;
}
if (endMatches > 0) {
blockCommentDepth -= endMatches;
sawBlockComment = true;
}
if (blockCommentDepth != 0 || sawBlockComment) {
continue;
}
// Check for a match with the language version.
final Match match = _languageVersion.matchAsPrefix(trimmedLine);
if (match != null) {
return trimmedLine;
}
// Check for a declaration which ends the search for a language
// version.
if (_declarationEnd.matchAsPrefix(trimmedLine) != null) {
break;
}
}
// If the language version cannot be found, use the package version.
if (package != null) {
return '// @dart = ${package.languageVersion}';
}
return null;
}
......@@ -5,14 +5,14 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'package:stream_channel/stream_channel.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/environment.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/plugin/platform_helpers.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/runner_suite.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/suite.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/plugin/platform_helpers.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/environment.dart'; // ignore: implementation_imports
import 'package:vm_service/vm_service.dart' as vm_service;
import '../base/common.dart';
import '../base/file_system.dart';
......@@ -20,6 +20,7 @@ import '../base/io.dart';
import '../build_info.dart';
import '../compile.dart';
import '../convert.dart';
import '../dart/language_version.dart';
import '../dart/package_map.dart';
import '../globals.dart' as globals;
import '../project.dart';
......@@ -151,7 +152,7 @@ String generateTestBootstrap({
@required InternetAddress host,
File testConfigFile,
bool updateGoldens = false,
bool nullSafety = false,
String languageVersionHeader = '',
}) {
assert(testUrl != null);
assert(host != null);
......@@ -164,6 +165,7 @@ String generateTestBootstrap({
final StringBuffer buffer = StringBuffer();
buffer.write('''
$languageVersionHeader
import 'dart:async';
import 'dart:convert'; // ignore: dart_convert_import
import 'dart:io'; // ignore: dart_io_import
......@@ -181,17 +183,11 @@ import '$testUrl' as test;
import '${Uri.file(testConfigFile.path)}' as test_config;
''');
}
// This type is sensitive to the non-nullable experiment.
final String beforeLoadTypedef = nullSafety
? 'Future<dynamic> Function()?'
: 'Future<dynamic> Function()';
buffer.write('''
/// Returns a serialized test suite.
StreamChannel<dynamic> serializeSuite(Function getMain(),
{bool hidePrints = true, $beforeLoadTypedef beforeLoad}) {
return RemoteListener.start(getMain,
hidePrints: hidePrints, beforeLoad: beforeLoad);
StreamChannel<dynamic> serializeSuite(Function getMain()) {
return RemoteListener.start(getMain);
}
/// Capture any top-level errors (mostly lazy syntax errors, since other are
......@@ -402,11 +398,17 @@ class FlutterPlatform extends PlatformPlugin {
@visibleForTesting
Future<HttpServer> bind(InternetAddress host, int port) => HttpServer.bind(host, port);
PackageConfig _packageConfig;
Future<_AsyncError> _startTest(
String testPath,
StreamChannel<dynamic> controller,
int ourTestCount,
) async {
_packageConfig ??= await loadPackageConfigWithLogging(
globals.fs.file(globalPackagesPath),
logger: globals.logger,
);
globals.printTrace('test $ourTestCount: starting test $testPath');
_AsyncError outOfBandError; // error that we couldn't send to the harness that we need to send via our future
......@@ -748,15 +750,20 @@ class FlutterPlatform extends PlatformPlugin {
Uri testUrl,
}) {
assert(testUrl.scheme == 'file');
final File file = globals.fs.file(testUrl);
return generateTestBootstrap(
testUrl: testUrl,
testConfigFile: findTestConfigFile(globals.fs.file(testUrl)),
host: host,
updateGoldens: updateGoldens,
nullSafety: extraFrontEndOptions?.contains('--enable-experiment=non-nullable') ?? false,
languageVersionHeader: determineLanguageVersion(
file,
_packageConfig[flutterProject?.manifest?.appName],
),
);
}
File _cachedFontConfig;
@override
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/dart/language_version.dart';
import 'package:package_config/package_config.dart';
import '../../src/common.dart';
void main() {
testWithoutContext('detects language version in comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), '// @dart = 2.9');
});
testWithoutContext('detects technically invalid language version', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
// @dart
''');
expect(determineLanguageVersion(file, null), '// @dart');
});
testWithoutContext('detects language version with leading whitespace', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), '// @dart = 2.9');
});
testWithoutContext('detects language version with tabs', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
//\t@dart = 2.9
''');
expect(determineLanguageVersion(file, null), '//\t@dart = 2.9');
});
testWithoutContext('detects language version with tons of whitespace', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
// @dart = 23
''');
expect(determineLanguageVersion(file, null), '// @dart = 23');
});
testWithoutContext('does not detect language version in dartdoc', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
/// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('does not detect language version in block comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
/*
// @dart = 2.9
*/
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('does not detect language version in nested block comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
/*
/*
// @dart = 2.9
*/
*/
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('detects language version after nested block comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
/* /*
*/
*/
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), '// @dart = 2.9');
});
testWithoutContext('does not crash with unbalanced opening block comments', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
/*
/*
*/
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('does not crash with unbalanced closing block comments', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
/*
*/
*/
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('does not detect language version in single line block comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
/* // @dart = 2.9 */
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('does not detect language version after import declaration', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
import 'dart:ui' as ui;
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('does not detect language version after part declaration', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
part of 'foo.dart';
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('does not detect language version after library declaration', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
library funstuff;
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), null);
});
testWithoutContext('looks up language version from package if not found in file', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
''');
final Package package = Package(
'foo',
Uri.parse('file://foo/'),
languageVersion: LanguageVersion(2, 7),
);
expect(determineLanguageVersion(file, package), '// @dart = 2.7');
});
}
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