|  | // Copyright (c) 2021, 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. | 
|  |  | 
|  | import 'dart:io'; | 
|  |  | 
|  | import 'package:testing/testing.dart' | 
|  | show Chain, ChainContext, Result, Step, TestDescription; | 
|  | import "package:yaml/yaml.dart" show YamlMap, loadYamlNode; | 
|  |  | 
|  | import 'utils/suite_utils.dart'; | 
|  | import 'parser_suite.dart' | 
|  | show ListenerStep, ParserTestListenerWithMessageFormatting; | 
|  | import 'testing_utils.dart' show checkEnvironment; | 
|  |  | 
|  | void main([List<String> arguments = const []]) => internalMain(createContext, | 
|  | arguments: arguments, | 
|  | displayName: "parser equivalence suite", | 
|  | configurationPath: "../testing.json"); | 
|  |  | 
|  | Future<Context> createContext( | 
|  | Chain suite, Map<String, String> environment) { | 
|  | const Set<String> knownEnvironmentKeys = {}; | 
|  | checkEnvironment(environment, knownEnvironmentKeys); | 
|  |  | 
|  | return new Future.value(new Context(suite.name)); | 
|  | } | 
|  |  | 
|  | class Context extends ChainContext { | 
|  | final String suiteName; | 
|  |  | 
|  | Context(this.suiteName); | 
|  |  | 
|  | @override | 
|  | final List<Step> steps = const <Step>[ | 
|  | const ListenerCompareStep(), | 
|  | ]; | 
|  | } | 
|  |  | 
|  | class ListenerCompareStep | 
|  | extends Step<TestDescription, TestDescription, Context> { | 
|  | const ListenerCompareStep(); | 
|  |  | 
|  | @override | 
|  | String get name => "listenerCompare"; | 
|  |  | 
|  | @override | 
|  | Future<Result<TestDescription>> run( | 
|  | TestDescription description, Context context) { | 
|  | Uri uri = description.uri; | 
|  | String contents = new File.fromUri(uri).readAsStringSync(); | 
|  | YamlMap yaml = loadYamlNode(contents, sourceUrl: uri) as YamlMap; | 
|  | List<Uri> files = | 
|  | (yaml["files"] as List).map((s) => uri.resolve(s)).toList(); | 
|  | Set<String> filters = new Set<String>.from(yaml["filters"] ?? []); | 
|  | Set<String> ignored = new Set<String>.from(yaml["ignored"] ?? []); | 
|  |  | 
|  | ParserTestListenerWithMessageFormatting? parserTestListenerFirst = | 
|  | ListenerStep.doListenerParsing( | 
|  | files[0], | 
|  | context.suiteName, | 
|  | description.shortName, | 
|  | ); | 
|  | if (parserTestListenerFirst == null) { | 
|  | return Future.value(crash(description, StackTrace.current)); | 
|  | } | 
|  |  | 
|  | for (int i = 1; i < files.length; i++) { | 
|  | ParserTestListenerWithMessageFormatting? parserTestListener = | 
|  | ListenerStep.doListenerParsing( | 
|  | files[i], | 
|  | context.suiteName, | 
|  | description.shortName, | 
|  | ); | 
|  | if (parserTestListener == null) { | 
|  | return Future.value(crash(description, StackTrace.current)); | 
|  | } | 
|  | String? compareResult = compare( | 
|  | parserTestListenerFirst, parserTestListener, filters, ignored); | 
|  | if (compareResult != null) { | 
|  | return Future.value( | 
|  | fail(description, compareResult, StackTrace.current)); | 
|  | } | 
|  | } | 
|  |  | 
|  | return new Future.value(new Result<TestDescription>.pass(description)); | 
|  | } | 
|  |  | 
|  | String? compare( | 
|  | ParserTestListenerWithMessageFormatting a, | 
|  | ParserTestListenerWithMessageFormatting b, | 
|  | Set<String> filters, | 
|  | Set<String> ignored) { | 
|  | List<String> aLines = a.sb.toString().split("\n"); | 
|  | List<String> bLines = b.sb.toString().split("\n"); | 
|  |  | 
|  | bool doRemoveListenerArguments = | 
|  | filters.contains("ignoreListenerArguments"); | 
|  |  | 
|  | int aIndex = 0; | 
|  | int bIndex = 0; | 
|  | while (aIndex < aLines.length && bIndex < bLines.length) { | 
|  | String aLine = aLines[aIndex]; | 
|  | String bLine = bLines[bIndex]; | 
|  | if (doRemoveListenerArguments) { | 
|  | aLine = removeListenerArguments(aLine); | 
|  | bLine = removeListenerArguments(bLine); | 
|  | } | 
|  | bool anyIgnored = false; | 
|  | if (ignored.contains(aLine.trim())) { | 
|  | anyIgnored = true; | 
|  | aIndex++; | 
|  | } | 
|  | if (ignored.contains(bLine.trim())) { | 
|  | anyIgnored = true; | 
|  | bIndex++; | 
|  | } | 
|  | if (anyIgnored) continue; | 
|  | if (aLine.trim() != bLine.trim()) { | 
|  | return "Disagreement: '${aLine}' vs '${bLine}'"; | 
|  | } | 
|  | aIndex++; | 
|  | bIndex++; | 
|  | } | 
|  |  | 
|  | // Any trailing lines? | 
|  | while (aIndex < aLines.length) { | 
|  | String aLine = aLines[aIndex]; | 
|  | if (doRemoveListenerArguments) { | 
|  | aLine = removeListenerArguments(aLine); | 
|  | } | 
|  | if (ignored.contains(aLine.trim())) { | 
|  | aIndex++; | 
|  | continue; | 
|  | } | 
|  | return "Unmatched line at end: '$aLine'"; | 
|  | } | 
|  | while (bIndex < bLines.length) { | 
|  | String bLine = bLines[bIndex]; | 
|  | if (doRemoveListenerArguments) { | 
|  | bLine = removeListenerArguments(bLine); | 
|  | } | 
|  | if (ignored.contains(bLine.trim())) { | 
|  | bIndex++; | 
|  | continue; | 
|  | } | 
|  | return "Unmatched line at end: '$bLine'"; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | String removeListenerArguments(String s) { | 
|  | int index = s.indexOf("("); | 
|  | if (index < 0) return s; | 
|  | return s.substring(0, index); | 
|  | } | 
|  | } |