|  | // 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. | 
|  |  | 
|  | import 'package:_fe_analyzer_shared/src/testing/annotated_code_helper.dart'; | 
|  | import 'package:_fe_analyzer_shared/src/testing/id.dart'; | 
|  | import 'package:_fe_analyzer_shared/src/testing/id_generation.dart'; | 
|  | import 'package:_fe_analyzer_shared/src/testing/id_testing.dart'; | 
|  | import 'package:_fe_analyzer_shared/src/testing/features.dart'; | 
|  |  | 
|  | const List<String> markers = ['a', 'b', 'c']; | 
|  | final Uri mainUri = Uri.parse('memory:main.dart'); | 
|  |  | 
|  | main() { | 
|  | testString('/*test*/'); | 
|  | testString(''' | 
|  | some code/*test*/some more code | 
|  | '''); | 
|  | testString('/*a.test*/'); | 
|  | testString(''' | 
|  | some code/*a.test*/some more code | 
|  | '''); | 
|  | testString('/*a|b.test*/'); | 
|  | testString(''' | 
|  | some code/*a|b.test*/some more code | 
|  | '''); | 
|  | testString('/*a|b|c.test*/', expectedResult: '/*test*/'); | 
|  | testString(''' | 
|  | some code/*a|b|c.test*/some more code | 
|  | ''', expectedResult: ''' | 
|  | some code/*test*/some more code | 
|  | '''); | 
|  | testString('/*a.test1*//*b.test2*//*c.test3*/'); | 
|  | testString('/*b.test2*//*a.test1*//*c.test3*/'); | 
|  | testString('/*a.test1*//*c.test3*//*b.test2*/'); | 
|  |  | 
|  | testString('some code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(0, IdKind.node): 'test', | 
|  | }, | 
|  | 'b': { | 
|  | new NodeId(0, IdKind.node): 'test', | 
|  | }, | 
|  | 'c': { | 
|  | new NodeId(0, IdKind.node): 'test', | 
|  | }, | 
|  | }, | 
|  | expectedResult: '/*test*/some code'); | 
|  |  | 
|  | testString('some code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(4, IdKind.node): 'test', | 
|  | }, | 
|  | 'b': { | 
|  | new NodeId(4, IdKind.node): 'test', | 
|  | }, | 
|  | 'c': { | 
|  | new NodeId(4, IdKind.node): 'test', | 
|  | }, | 
|  | }, | 
|  | expectedResult: 'some/*test*/ code'); | 
|  |  | 
|  | testString('some code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(0, IdKind.node): 'test', | 
|  | }, | 
|  | 'b': { | 
|  | new NodeId(0, IdKind.node): 'test', | 
|  | }, | 
|  | }, | 
|  | expectedResult: '/*a|b.test*/some code'); | 
|  |  | 
|  | testString('some code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(0, IdKind.node): 'test', | 
|  | }, | 
|  | }, | 
|  | expectedResult: '/*a.test*/some code'); | 
|  |  | 
|  | testString('', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(0, IdKind.node): 'test1', | 
|  | }, | 
|  | 'b': { | 
|  | new NodeId(0, IdKind.node): 'test2', | 
|  | }, | 
|  | 'c': { | 
|  | new NodeId(0, IdKind.node): 'test3', | 
|  | }, | 
|  | }, | 
|  | expectedResult: '/*a.test1*//*b.test2*//*c.test3*/'); | 
|  | testString('some code/*test*/some more code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(9, IdKind.node): 'test1', | 
|  | }, | 
|  | }, | 
|  | expectedResult: 'some code/*a.test1*//*b|c.test*/some more code'); | 
|  |  | 
|  | testString('some codesome more code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(9, IdKind.node): '', | 
|  | }, | 
|  | 'b': { | 
|  | new NodeId(9, IdKind.node): '', | 
|  | }, | 
|  | 'c': { | 
|  | new NodeId(9, IdKind.node): '', | 
|  | }, | 
|  | }, | 
|  | expectedResult: 'some codesome more code'); | 
|  |  | 
|  | testString('some codesome more code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(9, IdKind.node): '', | 
|  | }, | 
|  | 'b': { | 
|  | new NodeId(9, IdKind.node): '', | 
|  | }, | 
|  | }, | 
|  | expectedResult: 'some codesome more code'); | 
|  |  | 
|  | testString('some codesome more code', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(9, IdKind.node): '', | 
|  | }, | 
|  | }, | 
|  | expectedResult: 'some codesome more code'); | 
|  |  | 
|  | testString(''' | 
|  | some code | 
|  | /*member: memberName:test*/ | 
|  | some more code | 
|  | '''); | 
|  |  | 
|  | testString(''' | 
|  | some code | 
|  | /*member: memberName:test*/ | 
|  | some more code | 
|  | ''', actualData: { | 
|  | 'a': { | 
|  | new MemberId('memberName'): 'test1', | 
|  | } | 
|  | }, expectedResult: ''' | 
|  | some code | 
|  | /*a.member: memberName:test1*/ | 
|  | /*b|c.member: memberName:test*/ | 
|  | some more code | 
|  | '''); | 
|  |  | 
|  | testString('''some code | 
|  | /*a.member: memberName:test1*/ | 
|  | /*b|c.member: memberName:test*/ | 
|  | some more code | 
|  | ''', actualData: { | 
|  | 'b': { | 
|  | new MemberId('memberName'): 'test1', | 
|  | } | 
|  | }, expectedResult: ''' | 
|  | some code | 
|  | /*a|b.member: memberName:test1*/ | 
|  | /*c.member: memberName:test*/ | 
|  | some more code | 
|  | '''); | 
|  |  | 
|  | testString(''' | 
|  | some code | 
|  | /*a|b.member: memberName:test1*/ | 
|  | /*c.member: memberName:test*/ | 
|  | some more code | 
|  | ''', actualData: { | 
|  | 'c': { | 
|  | new MemberId('memberName'): 'test1', | 
|  | } | 
|  | }, expectedResult: ''' | 
|  | some code | 
|  | /*member: memberName:test1*/ | 
|  | some more code | 
|  | '''); | 
|  |  | 
|  | testString('/*test*/', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new NodeId(0, IdKind.node): 'test1', | 
|  | } | 
|  | }, | 
|  | expectedResult: '/*a.test1*//*b|c.test*/'); | 
|  |  | 
|  | testString('/*a.test1*//*b|c.test*/', | 
|  | actualData: { | 
|  | 'b': { | 
|  | new NodeId(0, IdKind.node): 'test1', | 
|  | } | 
|  | }, | 
|  | expectedResult: '/*a|b.test1*//*c.test*/'); | 
|  |  | 
|  | testString('/*a|b.test1*//*c.test*/', | 
|  | actualData: { | 
|  | 'c': { | 
|  | new NodeId(0, IdKind.node): 'test1', | 
|  | } | 
|  | }, | 
|  | expectedResult: '/*test1*/'); | 
|  |  | 
|  | testString('/*test*/', actualData: {'c': {}}, expectedResult: '/*a|b.test*/'); | 
|  |  | 
|  | testString('/*a|b.test*/', | 
|  | actualData: {'b': {}}, expectedResult: '/*a.test*/'); | 
|  |  | 
|  | testString('/*a.test*/', actualData: {'a': {}}, expectedResult: ''); | 
|  |  | 
|  | testString( | 
|  | ''' | 
|  | some code | 
|  | memberName() {} | 
|  | some more code | 
|  | ''', | 
|  | actualData: { | 
|  | 'a': {new MemberId('memberName'): 'test'} | 
|  | }, | 
|  | memberOffset: 10, | 
|  | expectedResult: ''' | 
|  | some code | 
|  | /*a.member: memberName:test*/ | 
|  | memberName() {} | 
|  | some more code | 
|  | '''); | 
|  |  | 
|  | testString( | 
|  | ''' | 
|  | some code | 
|  | void memberName() {} | 
|  | some more code | 
|  | ''', | 
|  | actualData: { | 
|  | 'a': {new MemberId('memberName'): 'test'} | 
|  | }, | 
|  | memberOffset: 15, | 
|  | expectedResult: ''' | 
|  | some code | 
|  | /*a.member: memberName:test*/ | 
|  | void memberName() {} | 
|  | some more code | 
|  | '''); | 
|  |  | 
|  | testString( | 
|  | ''' | 
|  | class Class { | 
|  | void memberName() {} | 
|  | } | 
|  | ''', | 
|  | actualData: { | 
|  | 'a': {new MemberId('memberName'): 'test'} | 
|  | }, | 
|  | memberOffset: 21, | 
|  | expectedResult: ''' | 
|  | class Class { | 
|  | /*a.member: memberName:test*/ | 
|  | void memberName() {} | 
|  | } | 
|  | '''); | 
|  |  | 
|  | testString( | 
|  | ''' | 
|  | class Class { | 
|  | void memberName() {} | 
|  | } | 
|  | ''', | 
|  | actualData: { | 
|  | 'a': { | 
|  | new ClassId('className'): 'test1', | 
|  | new MemberId('memberName'): 'test2', | 
|  | } | 
|  | }, | 
|  | classOffset: 6, | 
|  | memberOffset: 21, | 
|  | expectedResult: ''' | 
|  | /*a.class: className:test1*/ | 
|  | class Class { | 
|  | /*a.member: memberName:test2*/ | 
|  | void memberName() {} | 
|  | } | 
|  | '''); | 
|  |  | 
|  | testString( | 
|  | ''' | 
|  | // bla | 
|  | // bla | 
|  | // bla | 
|  |  | 
|  | class Class {} | 
|  | ''', | 
|  | actualData: { | 
|  | 'a': {new LibraryId(mainUri): 'test'} | 
|  | }, | 
|  | memberOffset: 15, | 
|  | expectedResult: ''' | 
|  | // bla | 
|  | // bla | 
|  | // bla | 
|  |  | 
|  | /*a.library: test*/ | 
|  |  | 
|  | class Class {} | 
|  | '''); | 
|  |  | 
|  | testFeatures(''' | 
|  | some code | 
|  | /*member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | '''); | 
|  | testFeatures(''' | 
|  | some code | 
|  | /*member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | ''', actualData: { | 
|  | 'a': { | 
|  | new MemberId('memberName'): 'test1=b,test2=[c,d],test3=e', | 
|  | } | 
|  | }, expectedResult: ''' | 
|  | some code | 
|  | /*a.member: memberName: | 
|  | test1=b, | 
|  | test2=[ | 
|  | c, | 
|  | d], | 
|  | test3=e | 
|  | */ | 
|  | /*b|c.member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | '''); | 
|  | // TODO(johnniwinther): Should new data reuse an existing encoding when that | 
|  | // differs from the pretty printed encoding? | 
|  | testFeatures(''' | 
|  | some code | 
|  | /*a.member: memberName:test1=b,test2=[c,d],test3=e*/ | 
|  | /*b|c.member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | ''', actualData: { | 
|  | 'b': { | 
|  | new MemberId('memberName'): 'test1=b,test2=[c,d],test3=e', | 
|  | } | 
|  | }, expectedResult: ''' | 
|  | some code | 
|  | /*a.member: memberName:test1=b,test2=[c,d],test3=e*/ | 
|  | /*b.member: memberName: | 
|  | test1=b, | 
|  | test2=[ | 
|  | c, | 
|  | d], | 
|  | test3=e | 
|  | */ | 
|  | /*c.member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | '''); | 
|  | testFeatures(''' | 
|  | some code | 
|  | /*a.member: memberName: | 
|  | test1=b, | 
|  | test2=[ | 
|  | c, | 
|  | d], | 
|  | test3=e | 
|  | */ | 
|  | /*b|c.member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | ''', actualData: { | 
|  | 'b': { | 
|  | new MemberId('memberName'): 'test1=b,test2=[c,d],test3=e', | 
|  | } | 
|  | }, expectedResult: ''' | 
|  | some code | 
|  | /*a|b.member: memberName: | 
|  | test1=b, | 
|  | test2=[ | 
|  | c, | 
|  | d], | 
|  | test3=e | 
|  | */ | 
|  | /*c.member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | '''); | 
|  | testFeatures(''' | 
|  | some code | 
|  | /*a|b.member: memberName: | 
|  | test1=b, | 
|  | test2=[ | 
|  | c, | 
|  | d], | 
|  | test3=e | 
|  | */ | 
|  | /*c.member: memberName: | 
|  | test1=a, | 
|  | test2=[ | 
|  | b, | 
|  | c], | 
|  | test3=d | 
|  | */ | 
|  | some more code | 
|  | ''', actualData: { | 
|  | 'c': { | 
|  | new MemberId('memberName'): 'test1=b,test2=[c,d],test3=e', | 
|  | } | 
|  | }, expectedResult: ''' | 
|  | some code | 
|  | /*member: memberName: | 
|  | test1=b, | 
|  | test2=[ | 
|  | c, | 
|  | d], | 
|  | test3=e | 
|  | */ | 
|  | some more code | 
|  | '''); | 
|  | } | 
|  |  | 
|  | void testString( | 
|  | String text, { | 
|  | Map<String, Map<Id, String>> actualData = const {}, | 
|  | String? expectedResult, | 
|  | int classOffset = 0, | 
|  | int memberOffset = 0, | 
|  | }) { | 
|  | testGeneral(const StringDataInterpreter(), text, | 
|  | actualData: actualData, | 
|  | expectedResult: expectedResult, | 
|  | classOffset: classOffset, | 
|  | memberOffset: memberOffset); | 
|  |  | 
|  | testFeatures(text, | 
|  | actualData: actualData, | 
|  | expectedResult: expectedResult, | 
|  | classOffset: classOffset, | 
|  | memberOffset: memberOffset); | 
|  | } | 
|  |  | 
|  | void testFeatures( | 
|  | String text, { | 
|  | Map<String, Map<Id, String>> actualData = const {}, | 
|  | String? expectedResult, | 
|  | int classOffset = 0, | 
|  | int memberOffset = 0, | 
|  | }) { | 
|  | Map<String, Map<Id, Features>> actualFeatures = {}; | 
|  | actualData.forEach((String marker, Map<Id, String> data) { | 
|  | Map<Id, Features> features = actualFeatures[marker] = {}; | 
|  | data.forEach((Id id, String text) { | 
|  | features[id] = Features.fromText(text.trim()); | 
|  | }); | 
|  | }); | 
|  | testGeneral(const FeaturesDataInterpreter(), text, | 
|  | actualData: actualFeatures, | 
|  | expectedResult: expectedResult, | 
|  | classOffset: classOffset, | 
|  | memberOffset: memberOffset); | 
|  | } | 
|  |  | 
|  | void testGeneral<T>(DataInterpreter<T> dataInterpreter, String text, | 
|  | {Map<String, Map<Id, T>> actualData = const {}, | 
|  | String? expectedResult, | 
|  | int classOffset = 0, | 
|  | int memberOffset = 0}) { | 
|  | expectedResult ??= text; | 
|  | AnnotatedCode code = | 
|  | new AnnotatedCode.fromText(text, commentStart, commentEnd); | 
|  | Map<String, MemberAnnotations<IdValue>> expectedMaps = {}; | 
|  | for (String marker in markers) { | 
|  | expectedMaps[marker] = new MemberAnnotations<IdValue>(); | 
|  | } | 
|  | computeExpectedMap(mainUri, mainUri.path, code, expectedMaps, | 
|  | onFailure: (String message) { | 
|  | throw message; | 
|  | }); | 
|  |  | 
|  | Map<String, Map<Uri, Map<Id, ActualData<T>>>> actualAnnotations = {}; | 
|  | actualData.forEach((String marker, Map<Id, T> data) { | 
|  | Map<Uri, Map<Id, ActualData<T>>> map = actualAnnotations[marker] = {}; | 
|  | Map<Id, ActualData<T>> actualData = map[mainUri] = {}; | 
|  | data.forEach((Id id, T value) { | 
|  | int offset; | 
|  | if (id is NodeId) { | 
|  | offset = id.value; | 
|  | } else if (id is MemberId) { | 
|  | offset = memberOffset; | 
|  | } else if (id is ClassId) { | 
|  | offset = classOffset; | 
|  | } else { | 
|  | offset = 0; | 
|  | } | 
|  | actualData[id] = new ActualData<T>(id, value, mainUri, offset, text); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | Map<Uri, List<Annotation>> annotations = computeAnnotationsPerUri<T>( | 
|  | {mainUri: code}, | 
|  | expectedMaps, | 
|  | mainUri, | 
|  | actualAnnotations, | 
|  | dataInterpreter); | 
|  | AnnotatedCode generated = new AnnotatedCode( | 
|  | code.annotatedCode, code.sourceCode, annotations[mainUri]!); | 
|  | String actualResult = generated.toText(); | 
|  | if (expectedResult != actualResult) { | 
|  | print("Unexpected result for '$text' with actualData=$actualData"); | 
|  | print('---expected-------------------------------------------------------'); | 
|  | print(expectedResult); | 
|  | print('---actual---------------------------------------------------------'); | 
|  | print(actualResult); | 
|  | throw StateError('Expected $expectedResult, got $actualResult'); | 
|  | } | 
|  | } |