| // Copyright (c) 2018, 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:async'; | |
| import 'package:analysis_server/src/computer/computer_folding.dart'; | |
| import 'package:analysis_server/src/protocol_server.dart'; | |
| import 'package:analyzer/dart/analysis/results.dart'; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| import '../../abstract_context.dart'; | |
| main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(FoldingComputerTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class FoldingComputerTest extends AbstractContextTest { | |
| String sourcePath; | |
| setUp() { | |
| super.setUp(); | |
| sourcePath = resourceProvider.convertPath('/p/lib/source.dart'); | |
| } | |
| test_single_import_directives() async { | |
| String content = """ | |
| import 'dart:async'; | |
| main() {} | |
| """; | |
| // Since there are no region comment markers above | |
| // just check the length instead of the contents | |
| final regions = await _computeRegions(content); | |
| expect(regions, hasLength(0)); | |
| } | |
| test_multiple_import_directives() async { | |
| String content = """ | |
| import/*1:INC*/ 'dart:async'; | |
| // We can have comments | |
| import 'package:a/b.dart'; | |
| import 'package:b/c.dart'; | |
| import '../a.dart';/*1:EXC:DIRECTIVES*/ | |
| main() {} | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_multiple_directive_types() async { | |
| String content = """ | |
| import/*1:INC*/ 'dart:async'; | |
| // We can have comments | |
| import 'package:a/b.dart'; | |
| import 'package:b/c.dart'; | |
| export '../a.dart';/*1:EXC:DIRECTIVES*/ | |
| main() {} | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_function() async { | |
| String content = """ | |
| // Content before | |
| main() {/*1:INC*/ | |
| print("Hello, world!"); | |
| /*1:INC:FUNCTION_BODY*/} | |
| // Content after | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_function_with_dart_doc() async { | |
| String content = """ | |
| // Content before | |
| /*1:EXC*//// This is a doc comment | |
| /// that spans lines/*1:INC:DOCUMENTATION_COMMENT*/ | |
| main() {/*2:INC*/ | |
| print("Hello, world!"); | |
| /*2:INC:FUNCTION_BODY*/} | |
| // Content after | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_nested_function() async { | |
| String content = """ | |
| // Content before | |
| main() {/*1:INC*/ | |
| doPrint() {/*2:INC*/ | |
| print("Hello, world!"); | |
| /*2:INC:FUNCTION_BODY*/} | |
| doPrint(); | |
| /*1:INC:FUNCTION_BODY*/} | |
| // Content after | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_class() async { | |
| String content = """ | |
| // Content before | |
| class Person {/*1:INC*/ | |
| Person() {/*2:INC*/ | |
| print("Hello, world!"); | |
| /*2:INC:FUNCTION_BODY*/} | |
| void sayHello() {/*3:INC*/ | |
| print("Hello, world!"); | |
| /*3:INC:FUNCTION_BODY*/} | |
| /*1:INC:CLASS_BODY*/} | |
| // Content after | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_annotations() async { | |
| String content = """ | |
| @myMultilineAnnotation/*1:INC*/( | |
| "this", | |
| "is a test" | |
| )/*1:EXC:ANNOTATIONS*/ | |
| main() {} | |
| @noFoldNecessary | |
| main2() {} | |
| @multipleAnnotations1/*2:INC*/( | |
| "this", | |
| "is a test" | |
| ) | |
| @multipleAnnotations2() | |
| @multipleAnnotations3/*2:EXC:ANNOTATIONS*/ | |
| main3() {} | |
| @noFoldsForSingleClassAnnotation | |
| class MyClass {} | |
| @folded.classAnnotation1/*3:INC*/() | |
| @foldedClassAnnotation2/*3:EXC:ANNOTATIONS*/ | |
| class MyClass2 {/*4:INC*/ | |
| @fieldAnnotation1/*5:INC*/ | |
| @fieldAnnotation2/*5:EXC:ANNOTATIONS*/ | |
| int myField; | |
| @getterAnnotation1/*6:INC*/ | |
| @getterAnnotation2/*6:EXC:ANNOTATIONS*/ | |
| int get myThing => 1; | |
| @setterAnnotation1/*7:INC*/ | |
| @setterAnnotation2/*7:EXC:ANNOTATIONS*/ | |
| void set myThing(int value) {} | |
| @methodAnnotation1/*8:INC*/ | |
| @methodAnnotation2/*8:EXC:ANNOTATIONS*/ | |
| void myMethod() {} | |
| @constructorAnnotation1/*9:INC*/ | |
| @constructorAnnotation1/*9:EXC:ANNOTATIONS*/ | |
| MyClass2() {} | |
| /*4:INC:CLASS_BODY*/} | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_file_header() async { | |
| String content = """ | |
| // Copyright some year by some people/*1:EXC*/ | |
| // See LICENCE etc./*1:INC:FILE_HEADER*/ | |
| // This is not the file header | |
| // It's just a comment | |
| main() {} | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_file_header_with_no_function_comment() async { | |
| String content = """ | |
| // Copyright some year by some people/*1:EXC*/ | |
| // See LICENCE etc./*1:INC:FILE_HEADER*/ | |
| main() {} | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_file_header_with_non_end_of_line_comment() async { | |
| String content = """ | |
| // Copyright some year by some people/*1:EXC*/ | |
| // See LICENCE etc./*1:INC:FILE_HEADER*/ | |
| /* This shouldn't be part of the file header */ | |
| main() {} | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_file_header_does_not_include_block_comments() async { | |
| String content = """ | |
| /* | |
| * Copyright some year by some people | |
| * See LICENCE etc. | |
| */ | |
| /* This shouldn't be part of the file header */ | |
| main() {} | |
| """; | |
| final regions = await _computeRegions(content); | |
| expect(regions, hasLength(0)); | |
| } | |
| test_file_header_with_script_prefix() async { | |
| String content = """ | |
| #! /usr/bin/dart | |
| // Copyright some year by some people/*1:EXC*/ | |
| // See LICENCE etc./*1:INC:FILE_HEADER*/ | |
| // This is not the file header | |
| // It's just a comment | |
| main() {} | |
| """; | |
| final regions = await _computeRegions(content); | |
| _compareRegions(regions, content); | |
| } | |
| test_comment_is_not_considered_file_header() async { | |
| String content = """ | |
| // This is not the file header | |
| // It's just a comment | |
| main() {} | |
| """; | |
| // Since there are no region comment markers above | |
| // just check the length instead of the contents | |
| final regions = await _computeRegions(content); | |
| expect(regions, hasLength(0)); | |
| } | |
| /// Compares provided folding regions with expected | |
| /// regions extracted from the comments in the provided content. | |
| void _compareRegions(List<FoldingRegion> regions, String content) { | |
| // Find all numeric markers for region starts. | |
| final regex = new RegExp(r'/\*(\d+):(INC|EXC)\*/'); | |
| final expectedRegions = regex.allMatches(content); | |
| // Check we didn't get more than expected, since the loop below only | |
| // checks for the presence of matches, not absence. | |
| expect(regions, hasLength(expectedRegions.length)); | |
| // Go through each marker, find the expected region start/end and | |
| // ensure it's in the results. | |
| expectedRegions.forEach((m) { | |
| final i = m.group(1); | |
| final inclusiveStart = m.group(2) == "INC"; | |
| // Find the end marker. | |
| final endMatch = | |
| new RegExp('/\\*$i:(INC|EXC):(.+?)\\*/').firstMatch(content); | |
| final inclusiveEnd = endMatch.group(1) == "INC"; | |
| final expectedKindString = endMatch.group(2); | |
| final expectedKind = FoldingKind.VALUES.firstWhere( | |
| (f) => f.toString() == 'FoldingKind.$expectedKindString', | |
| orElse: () => throw new Exception( | |
| "Annotated test code references $expectedKindString but " | |
| "this does not exist in FoldingKind")); | |
| final expectedStart = inclusiveStart ? m.start : m.end; | |
| final expectedLength = | |
| (inclusiveEnd ? endMatch.end : endMatch.start) - expectedStart; | |
| expect( | |
| regions, | |
| contains( | |
| new FoldingRegion(expectedKind, expectedStart, expectedLength))); | |
| }); | |
| } | |
| Future<List<FoldingRegion>> _computeRegions(String sourceContent) async { | |
| newFile(sourcePath, content: sourceContent); | |
| ResolveResult result = await driver.getResult(sourcePath); | |
| DartUnitFoldingComputer computer = | |
| new DartUnitFoldingComputer(result.lineInfo, result.unit); | |
| return computer.compute(); | |
| } | |
| } |