|
2 | 2 | // Use of this source code is governed by a BSD-style license that can be |
3 | 3 | // found in the LICENSE file. |
4 | 4 |
|
| 5 | +import 'dart:io'; |
5 | 6 | import 'dart:ui'; |
6 | 7 |
|
| 8 | +import 'package:analyzer/dart/analysis/features.dart'; |
| 9 | +import 'package:analyzer/dart/analysis/results.dart'; |
| 10 | +import 'package:analyzer/dart/analysis/utilities.dart'; |
| 11 | +import 'package:analyzer/dart/ast/ast.dart'; |
7 | 12 | import 'package:litetest/litetest.dart'; |
8 | 13 |
|
9 | 14 | void main() { |
@@ -39,4 +44,60 @@ void main() { |
39 | 44 | return ViewConfiguration(view: view)..copyWith(view: view, window: view); |
40 | 45 | }); |
41 | 46 | }); |
| 47 | + |
| 48 | + test("PlatformDispatcher and PlatformConfiguration have matching getters", () { |
| 49 | + final List<Pattern> exclusionCriteria = <Pattern>[ |
| 50 | + RegExp(r'^_.*'), // exclude any private members |
| 51 | + RegExp(r'^on[A-Z].*'), // exclude any callbacks |
| 52 | + RegExp(r'locale$'), // locale is a convenience getter for interacting with `locales` which _is_ backed by the config |
| 53 | + 'instance', // exclude the static instance of PlatformDispatcher |
| 54 | + 'configuration', // the configuration should not reference itself |
| 55 | + 'views', // views are backed by their own config and don't need to be referenced in the platform config |
| 56 | + 'initialLifecycleState', // default route is stored/retrieved from the platform, not the engine |
| 57 | + 'frameData', // framed data updates too often and would kick off too many config changes |
| 58 | + ]; |
| 59 | + |
| 60 | + final String flutterDir = Platform.environment['FLUTTER_DIR']!; |
| 61 | + final List<ClassDeclaration> classes = parseFile( |
| 62 | + path: '$flutterDir/lib/ui/platform_dispatcher.dart', |
| 63 | + featureSet: FeatureSet.latestLanguageVersion(), |
| 64 | + throwIfDiagnostics: false, |
| 65 | + ).unit.declarations.whereType<ClassDeclaration>().toList(); |
| 66 | + final ClassDeclaration dispatcherClass = classes.singleWhere((ClassDeclaration c) => c.name.toString() == (PlatformDispatcher).toString()); |
| 67 | + final ClassDeclaration configurationClass = classes.singleWhere((ClassDeclaration c) => c.name.toString() == (PlatformConfiguration).toString()); |
| 68 | + |
| 69 | + Iterable<String> getNameOfClassMember(ClassMember member) { |
| 70 | + if (member is MethodDeclaration) { |
| 71 | + return <String>[member.name.toString()]; |
| 72 | + }else if (member is FieldDeclaration) { |
| 73 | + return member.fields.variables.map((VariableDeclaration variable) => variable.name.toString()); |
| 74 | + } else { |
| 75 | + return []; |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + final Set<String> dispatcherMembers = dispatcherClass.members |
| 80 | + .where((ClassMember member) => |
| 81 | + member is FieldDeclaration || |
| 82 | + member is MethodDeclaration && member.isGetter) |
| 83 | + .expand<String>(getNameOfClassMember) |
| 84 | + .where((String name) => |
| 85 | + exclusionCriteria.every((criteria) => |
| 86 | + criteria.allMatches(name).isEmpty)) |
| 87 | + .toSet(); |
| 88 | + final Set<String> configurationMembers = configurationClass.members |
| 89 | + .where((ClassMember member) => |
| 90 | + member is FieldDeclaration || |
| 91 | + member is MethodDeclaration && member.isGetter) |
| 92 | + .expand(getNameOfClassMember) |
| 93 | + .toSet(); |
| 94 | + |
| 95 | + final Set<String> missingMembers = configurationMembers.difference(dispatcherMembers); |
| 96 | + final Set<String> extraMembers = dispatcherMembers.difference(configurationMembers); |
| 97 | + expect(missingMembers.isEmpty && extraMembers.isEmpty, isTrue, |
| 98 | + reason: |
| 99 | + 'PlatformDispatcher is out of sync with PlatformConfiguration.\n' |
| 100 | + ' Missing Members: $missingMembers\n' |
| 101 | + ' Members not backed by PlatformConfiguration: $extraMembers'); |
| 102 | + }); |
42 | 103 | } |
0 commit comments