| // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library fasta.test.textual_outline_test; |
| |
| import 'dart:io'; |
| import 'dart:typed_data'; |
| |
| import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart' |
| show ScannerConfiguration; |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart'; |
| import 'package:dart_style/dart_style.dart' show DartFormatter; |
| import 'package:front_end/src/api_prototype/experimental_flags.dart'; |
| import 'package:front_end/src/util/textual_outline.dart'; |
| import 'package:testing/testing.dart' |
| show |
| Chain, |
| ChainContext, |
| Expectation, |
| ExpectationSet, |
| Result, |
| Step, |
| TestDescription; |
| |
| import '../utils/kernel_chain.dart' show MatchContext; |
| import 'suite_utils.dart'; |
| import '../testing/environment_keys.dart'; |
| import '../testing/folder_options.dart'; |
| |
| const int minSupportedMajorVersion = 2; |
| const int minSupportedMinorVersion = 12; |
| |
| const List<Map<String, String>> EXPECTATIONS = [ |
| { |
| "name": "ExpectationFileMismatch", |
| "group": "Fail", |
| }, |
| { |
| "name": "ExpectationFileMissing", |
| "group": "Fail", |
| }, |
| { |
| "name": "EmptyOutput", |
| "group": "Fail", |
| }, |
| { |
| "name": "UnknownChunk", |
| "group": "Fail", |
| }, |
| { |
| "name": "FormatterCrash", |
| "group": "Fail", |
| }, |
| ]; |
| |
| Future<Context> createContext(Chain suite, Map<String, String> environment) { |
| return new Future.value(new Context(suite.root, environment)); |
| } |
| |
| void main([List<String> arguments = const []]) => internalMain( |
| createContext, |
| arguments: arguments, |
| displayName: "textual outline suite", |
| ); |
| |
| class Context extends ChainContext with MatchContext { |
| final SuiteFolderOptions suiteFolderOptions; |
| final Map<ExperimentalFlag, bool> forcedExperimentalFlags; |
| |
| @override |
| final bool updateExpectations; |
| |
| @override |
| String get updateExpectationsOption => |
| '${EnvironmentKeys.updateExpectations}=true'; |
| |
| @override |
| bool get canBeFixWithUpdateExpectations => true; |
| |
| Context(Uri baseUri, Map<String, String> environment) |
| : suiteFolderOptions = new SuiteFolderOptions(baseUri), |
| updateExpectations = |
| environment[EnvironmentKeys.updateExpectations] == "true", |
| forcedExperimentalFlags = |
| SuiteFolderOptions.computeForcedExperimentalFlags(environment); |
| |
| @override |
| final List<Step> steps = const <Step>[ |
| const TextualOutline(), |
| ]; |
| |
| @override |
| final ExpectationSet expectationSet = |
| new ExpectationSet.fromJsonList(EXPECTATIONS); |
| } |
| |
| class TextualOutline extends Step<TestDescription, TestDescription, Context> { |
| const TextualOutline(); |
| |
| @override |
| String get name => "TextualOutline"; |
| |
| @override |
| Future<Result<TestDescription>> run( |
| TestDescription description, Context context) async { |
| FolderOptions folderOptions = |
| context.suiteFolderOptions.computeFolderOptions(description); |
| Map<ExperimentalFlag, bool> experimentalFlags = folderOptions |
| .computeExplicitExperimentalFlags(context.forcedExperimentalFlags); |
| Map<ExperimentalFlag, bool> experimentalFlagsExplicit = |
| folderOptions.computeExplicitExperimentalFlags(const {}); |
| |
| Uint8List bytes = new File.fromUri(description.uri).readAsBytesSync(); |
| for (bool modelled in [false, true]) { |
| TextualOutlineInfoForTesting info = new TextualOutlineInfoForTesting(); |
| String? result = textualOutline( |
| bytes, |
| new ScannerConfiguration( |
| enableExtensionMethods: isExperimentEnabled( |
| ExperimentalFlag.extensionMethods, |
| explicitExperimentalFlags: experimentalFlags), |
| enableNonNullable: isExperimentEnabled(ExperimentalFlag.nonNullable, |
| explicitExperimentalFlags: experimentalFlags), |
| enableTripleShift: isExperimentEnabled(ExperimentalFlag.tripleShift, |
| explicitExperimentalFlags: experimentalFlags), |
| ), |
| throwOnUnexpected: true, |
| performModelling: modelled, |
| returnNullOnError: false, |
| enablePatterns: isExperimentEnabled(ExperimentalFlag.patterns, |
| explicitExperimentalFlags: experimentalFlags), |
| infoForTesting: info, |
| ); |
| if (result == null) { |
| return new Result( |
| null, context.expectationSet["EmptyOutput"], description.uri); |
| } |
| |
| bool containsUnknownChunk = info.hasUnknownChunk; |
| bool tryFormat = !containsUnknownChunk; |
| for (LanguageVersionToken version in info.languageVersionTokens) { |
| if (version.major < minSupportedMajorVersion) { |
| tryFormat = false; |
| } else if (version.major == minSupportedMajorVersion && |
| version.minor < minSupportedMinorVersion) { |
| tryFormat = false; |
| } |
| } |
| dynamic formatterException; |
| StackTrace? formatterExceptionSt; |
| if (tryFormat) { |
| try { |
| List<String> experimentFlags = []; |
| for (MapEntry<ExperimentalFlag, bool> entry |
| in experimentalFlags.entries) { |
| if (entry.value) { |
| experimentFlags.add(entry.key.name); |
| } |
| } |
| |
| // Default to the latest language version. If the test should be at |
| // an older language version, it will contain a `// @dart=x.y` |
| // comment, which takes precedence over this argument. |
| result = new DartFormatter( |
| languageVersion: DartFormatter.latestLanguageVersion, |
| experimentFlags: experimentFlags) |
| .format(result); |
| } catch (e, st) { |
| formatterException = e; |
| formatterExceptionSt = st; |
| } |
| } |
| |
| String filename = ".textual_outline.expect"; |
| if (modelled) { |
| filename = ".textual_outline_modelled.expect"; |
| } |
| |
| Result<TestDescription> expectMatch = |
| await context.match<TestDescription>( |
| filename, result!, description.uri, description); |
| if (expectMatch.outcome != Expectation.pass) return expectMatch; |
| |
| if (containsUnknownChunk) { |
| return new Result( |
| null, context.expectationSet["UnknownChunk"], description.uri); |
| } |
| |
| if (formatterException != null && !info.hasParserErrors) { |
| bool hasUnreleasedExperiment = false; |
| for (MapEntry<ExperimentalFlag, bool> entry |
| in experimentalFlagsExplicit.entries) { |
| if (entry.value) { |
| if (!entry.key.isEnabledByDefault) { |
| hasUnreleasedExperiment = true; |
| break; |
| } |
| } |
| } |
| if (!hasUnreleasedExperiment) { |
| return new Result(null, context.expectationSet["FormatterCrash"], |
| formatterException, |
| trace: formatterExceptionSt); |
| } |
| } |
| } |
| |
| return new Result.pass(description); |
| } |
| } |