/* * Copyright 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.ui.internal.text.functions; import com.google.dart.tools.ui.DartToolsPlugin; import com.google.dart.tools.ui.text.DartPartitions; import com.google.dart.tools.ui.text.DartTextTools; import junit.framework.TestCase; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocumentPartitioner; import org.eclipse.jface.text.ITypedRegion; public class FastDartPartitionScannerTest extends TestCase implements DartPartitions { /** * The default partition content type, used to represent "plain" code. */ private static final String DEFAULT_TYPE = "__dftl_partition_content_type"; public void test_FastDartPartitionScanner_blockComment() { // class X { /* comment */ } assertPartitions( // "class X {\n", DEFAULT_TYPE, // "/* comment */", DART_MULTI_LINE_COMMENT, // "\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_blockComment_inInterpolation() { // class X { var s="xxx ${yyy /* comment */ + zzz} xxx"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"xxx ", DART_STRING, // "${yyy ", DEFAULT_TYPE, // "/* comment */", DART_MULTI_LINE_COMMENT, // " + zzz}", DEFAULT_TYPE, // " xxx\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_blockComment_nested() { // class X { /* a/one /* comment /* nested */ inside */ another */ } assertPartitions( // "class X {\n", DEFAULT_TYPE, // "/* a/one /* comment /* nested */ inside */ another */", DART_MULTI_LINE_COMMENT, // "\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_blockComment_nested_unclosed() { // class X { /* a/one /* comment /* nested */ inside */ } assertPartitions( // "class X {\n", DEFAULT_TYPE, // "/* a/one /* comment /* nested */ inside */\n}\n", DART_MULTI_LINE_COMMENT // ); } public void test_FastDartPartitionScanner_blockComment_unclosed() { // class X { /* comment assertPartitions( // "class X {\n", DEFAULT_TYPE, // "/* comment", DART_MULTI_LINE_COMMENT // ); } public void test_FastDartPartitionScanner_defaultType() { // class X {} assertPartitions( // "class X {}", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_defaultType_insertion() { // class X {} assertPartitionsAfter( // "class X {}", // "class X {\n }", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_docComment() { // class X { /** comment */ var s=null; } assertPartitions( // "class X {\n", DEFAULT_TYPE, // "/** comment */", DART_DOC, // "\nvar s = null;}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_docComment_unfinishedToFinished() { assertPartitionsAfter("/** *\nvoid main() {}", // "/** */", DART_DOC, // "void main() {}", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_doubleLineComment() { // //// assertPartitions( // "////", DART_SINGLE_LINE_COMMENT // ); } public void test_FastDartPartitionScanner_endOfLineComment() { // class X { // comment } assertPartitions( // "class X {\n", DEFAULT_TYPE, // "// comment\n", DART_SINGLE_LINE_COMMENT, // "}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_endOfLineComment_inInterpolation() { // class X { var s="xxx ${yyy // comment + zzz} xxx"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"xxx ", DART_STRING, // "${yyy ", DEFAULT_TYPE, // "// comment\n", DART_SINGLE_LINE_COMMENT, // " + zzz}", DEFAULT_TYPE, // " xxx\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_endOfLineDocComment() { // class X { /// comment } assertPartitions( // "class X {\n", DEFAULT_TYPE, // "/// comment\n", DART_SINGLE_LINE_DOC, // "}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_endOfLineDocComment_inInterpolation() { // class X { var s="xxx ${yyy /// comment + zzz} xxx"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"xxx ", DART_STRING, // "${yyy ", DEFAULT_TYPE, // "/// comment\n", DART_SINGLE_LINE_DOC, // " + zzz}", DEFAULT_TYPE, // " xxx\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_multilineString_double() { // class X { var s="""test"""; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"\"\"test\"\"\"", DART_MULTI_LINE_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_multilineString_insertion() { assertPartitionsAfter( "void main() {\n var s = '''<tr>\n <td><div class=\"${getBox(message.selected)}\"></div></td>\n <td><div class=\"${getBox(message.starred)}\"></div></td>\n </tr>'''\n}", // "void main() {\n var s = ", DEFAULT_TYPE, // "'''<tr>\n <td><div class=\"", DART_MULTI_LINE_STRING, // "${getBox(message.selected)}", DEFAULT_TYPE, // "\"></div></td>\n\n <td><div class=\"", DART_MULTI_LINE_STRING, // "${getBox(message.starred)}", DEFAULT_TYPE, // "\"></div></td>\n </tr>'''", DART_MULTI_LINE_STRING, // "\n}", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_multilineString_single() { // class X { var s='''test'''; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "'''test'''", DART_MULTI_LINE_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_nestedBraces() { // class X { var s="xxx ${f((v) {return v;})} xxx"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"xxx ", DART_STRING, // "${f((v) {return v;})}", DEFAULT_TYPE, // " xxx\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_nestedStrings() { // class X { var s="xxx ${yyy 'zzz' """aaa ${bbb '''ccc''' bbb} aaa""" yyy} xxx"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"xxx ", DART_STRING, // "${yyy ", DEFAULT_TYPE, // "'zzz'", DART_STRING, // " ", DEFAULT_TYPE, // "\"\"\"aaa ", DART_MULTI_LINE_STRING, // "${bbb ", DEFAULT_TYPE, // "'''ccc'''", DART_MULTI_LINE_STRING, // " bbb}", DEFAULT_TYPE, // " aaa\"\"\"", DART_MULTI_LINE_STRING, // " yyy}", DEFAULT_TYPE, // " xxx\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_nestedStrings_with_comments() { // class X { var s="xxx ${yyy 'zzz' /* comment */ """aaa ${bbb '''ccc'''/* comment/*nested*/*/ bbb} aaa""" yyy} xxx"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"xxx ", DART_STRING, // "${yyy ", DEFAULT_TYPE, // "'zzz'", DART_STRING, // " ", DEFAULT_TYPE, // "/* comment */", DART_MULTI_LINE_COMMENT, // " ", DEFAULT_TYPE, // "\"\"\"aaa ", DART_MULTI_LINE_STRING, // "${bbb ", DEFAULT_TYPE, // "'''ccc'''", DART_MULTI_LINE_STRING, // "/* comment/*nested*/*/", DART_MULTI_LINE_COMMENT, // " bbb}", DEFAULT_TYPE, // " aaa\"\"\"", DART_MULTI_LINE_STRING, // " yyy}", DEFAULT_TYPE, // " xxx\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_normalString_atEOF() { // class X { var s=' assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "'", DART_STRING // ); } public void test_FastDartPartitionScanner_normalString_backToBackInterpolations() { // class X { return newName(id, '${prefix}${prefixes[prefix]++}'); } assertPartitions( // "class X {\nreturn newName(id, ", DEFAULT_TYPE, // "'", DART_STRING, // "${prefix}${prefixes[prefix]++}", DEFAULT_TYPE, // "'", DART_STRING, // ");\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_normalString_double() { // class X { var s="test"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "\"test\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_normalString_single() { // class X { var s='test'; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "'test'", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_normalString_unclosed() { // class X { var s='xxxyyy; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "'xxxyyy;", DART_STRING, // "\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_normalString_withEscapes() { // class X { var s=' \$$foo '; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "' \\$", DART_STRING, // "$foo", DEFAULT_TYPE, // " '", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_rawString_double() { // class X { var s=@"test"; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "r\"test\"", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_rawString_single() { // class X { var s=@'test'; } assertPartitions( // "class X {\nvar s=", DEFAULT_TYPE, // "r'test'", DART_STRING, // ";\n}\n", DEFAULT_TYPE // ); } public void test_FastDartPartitionScanner_rawString_withEscape() { // final str = @'\'; // comment assertPartitions( // "final str = ", DEFAULT_TYPE, // "r'\\'", DART_STRING, // "; ", DEFAULT_TYPE, // "// comment", DART_SINGLE_LINE_COMMENT // ); } /** * Assert that the given array of regions contains the expected number of elements. * * @param expectedCount the expected number of elements * @param regions the array being tested */ private void assertCount(int expectedCount, ITypedRegion[] regions) { assertEquals("wrong number of partitions:", expectedCount, regions.length); } /** * Given an array containing pairs of strings of the form * * <pre> * [codeSnippet1, partitionType1, codeSnippet2, partitionType2, ..., codeSnippetN, partitionTypeN] * </pre> * * where each code snippet should be a single partition of the given type, compose the snippets * into a single source string, partition it, and compare the results to see whether they have the * expected type and position. * * @param strings an array containing the (snippet, partition type) pairs */ private void assertPartitions(String... strings) { int stringCount = strings.length; assertTrue(stringCount % 2 == 0); int expectedCount = stringCount / 2; StringBuilder builder = new StringBuilder(); for (int i = 0; i < expectedCount; i++) { builder.append(strings[i * 2]); } ITypedRegion[] regions = partition(builder.toString()); assertCount(expectedCount, regions); int start = 0; for (int i = 0; i < expectedCount; i++) { int length = strings[i * 2].length(); assertRegion(regions[i], strings[i * 2 + 1], start, length); start += length; } } /** * Given an array containing pairs of strings of the form * * <pre> * [codeSnippet1, partitionType1, codeSnippet2, partitionType2, ..., codeSnippetN, partitionTypeN] * </pre> * * where each code snippet should be a single partition of the given type, compose the snippets * into a single source string, partition it, and compare the results to see whether they have the * expected type and position. * * @param previousContent the content of the document prior to the composed source being tested * @param strings an array containing the (snippet, partition type) pairs */ private void assertPartitionsAfter(String previousContent, String... strings) { Document doc = new Document(previousContent); DartTextTools tools = DartToolsPlugin.getDefault().getDartTextTools(); IDocumentPartitioner partitioner = tools.createDocumentPartitioner(); doc.setDocumentPartitioner(DartPartitions.DART_PARTITIONING, partitioner); partitioner.connect(doc); partitioner.computePartitioning(0, previousContent.length()); int stringCount = strings.length; assertTrue(stringCount % 2 == 0); int expectedCount = stringCount / 2; StringBuilder builder = new StringBuilder(); for (int i = 0; i < expectedCount; i++) { builder.append(strings[i * 2]); } String source = builder.toString(); int[] range = findInsertionRange(previousContent, source); ITypedRegion[] regions; int firstRegionIndex = 0; int start = 0; if (range == null) { doc.set(source); regions = partitioner.computePartitioning(0, source.length()); } else { try { doc.replace(range[0], 0, source.substring(range[0], range[1])); } catch (BadLocationException exception) { doc.set(source); } regions = partitioner.computePartitioning(range[0], source.length() - range[0]); int nextStart = start + strings[firstRegionIndex * 2].length(); while (nextStart < range[0]) { start = nextStart; firstRegionIndex++; nextStart = start + strings[firstRegionIndex * 2].length(); } } //doc.set(source); //ITypedRegion[] regions = part.computePartitioning(0, source.length()); assertCount(expectedCount - firstRegionIndex, regions); int nextRegionIndex = firstRegionIndex; if (range != null) { int length = strings[firstRegionIndex * 2].length() - range[0] + start; assertRegion(regions[0], strings[firstRegionIndex * 2 + 1], range[0], length); start = range[0] + length; nextRegionIndex++; } for (int i = nextRegionIndex; i < expectedCount; i++) { int length = strings[i * 2].length(); assertRegion(regions[i - firstRegionIndex], strings[i * 2 + 1], start, length); start += length; } } /** * Assert that the given region has the given type. If the offset and/or length is positive, * additionally assert that it has the given offset and length. * * @param region the region being tested * @param type the expected type of the region * @param offset the expected offset of the region, or -1 if the offset is to be ignored * @param length the expected length of the region, or -1 if the length is to be ignored */ private void assertRegion(ITypedRegion region, String type, int offset, int length) { assertEquals("wrong type:", type, region.getType()); if (offset >= 0) { if (offset != region.getOffset()) { System.out.print(""); } assertEquals("wrong offset:", offset, region.getOffset()); } if (length >= 0) { assertEquals("wrong length:", length, region.getLength()); } } /** * Return the range of characters that were inserted to convert from the previous source to the * current source, or <code>null</code> if there is not a single substring that was inserted to * get from the previous to current source. * * @param previousSource the previous source to which characters have been inserted * @param currentSource the current source after the characters have been inserted * @return the range of characters that were inserted */ private int[] findInsertionRange(String previousSource, String currentSource) { int length = previousSource.length(); if (length >= currentSource.length()) { return null; } int index = 0; while (index < length && previousSource.charAt(index) == currentSource.charAt(index)) { index++; } String suffix = previousSource.substring(index); if (currentSource.endsWith(suffix)) { return new int[] {index, currentSource.length() - suffix.length()}; } return null; } /** * Create partitions for the given source string. * * @param source the source string to be partitioned * @return the partitions that were created */ private ITypedRegion[] partition(String source) { Document doc = new Document(source); DartTextTools tools = DartToolsPlugin.getDefault().getDartTextTools(); IDocumentPartitioner part = tools.createDocumentPartitioner(); doc.setDocumentPartitioner(DartPartitions.DART_PARTITIONING, part); part.connect(doc); return part.computePartitioning(0, source.length()); } }