// 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 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> 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> actualData: const {}, String? expectedResult, int classOffset: 0, int memberOffset: 0, }) { Map> actualFeatures = {}; actualData.forEach((String marker, Map data) { Map 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(DataInterpreter dataInterpreter, String text, {Map> actualData: const {}, String? expectedResult, int classOffset: 0, int memberOffset: 0}) { expectedResult ??= text; AnnotatedCode code = new AnnotatedCode.fromText(text, commentStart, commentEnd); Map> expectedMaps = {}; for (String marker in markers) { expectedMaps[marker] = new MemberAnnotations(); } computeExpectedMap(mainUri, mainUri.path, code, expectedMaps, onFailure: (String message) { throw message; }); Map>>> actualAnnotations = {}; actualData.forEach((String marker, Map data) { Map>> map = actualAnnotations[marker] = {}; Map> 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(id, value, mainUri, offset, text); }); }); Map> annotations = computeAnnotationsPerUri( {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'" // ignore: unnecessary_null_comparison "${actualData != null ? ' with actualData=$actualData' : ''}"); print('---expected-------------------------------------------------------'); print(expectedResult); print('---actual---------------------------------------------------------'); print(actualResult); throw StateError('Expected $expectedResult, got $actualResult'); } }