| // 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'); |
| } |
| } |