| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // This program and the accompanying materials are made available |
| // under the terms of the Eclipse Public License v2.0 which accompanies |
| // this distribution, and is available at |
| // https://www.eclipse.org/legal/epl-2.0/ |
| |
| package org.eclipse.wst.jsdt.chromium.debug.core.sourcemap; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Random; |
| |
| import junit.framework.Assert; |
| |
| import org.eclipse.wst.jsdt.chromium.debug.core.model.StringMappingData; |
| import org.eclipse.wst.jsdt.chromium.debug.core.model.VmResourceId; |
| import org.eclipse.wst.jsdt.chromium.debug.core.sourcemap.SourcePositionMap.TranslateDirection; |
| import org.eclipse.wst.jsdt.chromium.debug.core.sourcemap.SourcePositionMapBuilder.CannotAddException; |
| import org.eclipse.wst.jsdt.chromium.debug.core.sourcemap.SourcePositionMapBuilder.MappingHandle; |
| import org.eclipse.wst.jsdt.chromium.debug.core.sourcemap.SourcePositionMapBuilder.ResourceSection; |
| import org.junit.Test; |
| |
| public class PositionMapBuilderImplTest { |
| /** |
| * Creates a sample position map and checks several points. |
| */ |
| @Test |
| public void basicTest() throws CannotAddException { |
| SourcePositionMapBuilder builder = new PositionMapBuilderImpl(); |
| builder.addMapping(new ResourceSection(new VmResourceId("source1.js", null), 0, 0, 5, 0), |
| new ResourceSection(new VmResourceId("compiled.js", null), 0, 0, 1, 0), |
| new TextSectionMappingImpl( |
| new StringMappingData(new int [] { 0,0, 1,0, 2,0, 3,0, 4,0 } , 5, 0), |
| new StringMappingData(new int [] { 0,0, 0,10, 0,20, 0,30, 0,40 }, 0, 50))); |
| |
| builder.addMapping(new ResourceSection(new VmResourceId("source2.js", null), 0, 0, 5, 0), |
| new ResourceSection(new VmResourceId("compiled.js", null), 1, 0, 2, 0), |
| new TextSectionMappingImpl( |
| new StringMappingData(new int [] { 0,0, 1,0, 2,0, 3,0, 4,0}, 5, 0), |
| new StringMappingData(new int [] { 1,0, 1,10, 1,20, 1,30, 1,40}, 1, 50))); |
| |
| builder.addMapping(new ResourceSection(new VmResourceId("source3.js", null), 0, 0, 5, 0), |
| new ResourceSection(new VmResourceId("compiled.js", null), 2, 0, 3, 0), |
| new TextSectionMappingImpl( |
| new StringMappingData(new int [] {0,0, 1,0, 2,0, 3,0, 4,0}, 5, 0), |
| new StringMappingData(new int [] {2,0, 2,10, 2,20, 2,30, 2,40}, 2, 50))); |
| |
| SourcePositionMap map = builder.getSourcePositionMap(); |
| |
| checkTwoWay(map, "other.js", 17, 4, "other.js", 17, 4, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 2, 11, "source3.js", 1, 1, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 0, 0, "source1.js", 0, 0, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 0, 1, "source1.js", 0, 1, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 1, 0, "source2.js", 0, 0, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 1, 3, "source2.js", 0, 3, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 2, 0, "source3.js", 0, 0, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 2, 3, "source3.js", 0, 3, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 3, 0, "compiled.js", 3, 0, TranslateDirection.VM_TO_USER); |
| |
| checkTwoWay(map, "compiled.js", 3, 3, "compiled.js", 3, 3, TranslateDirection.VM_TO_USER); |
| } |
| |
| private static SourcePosition checkOneWay(SourcePositionMap map, |
| String fromFile, int fromLine, int fromColumn, |
| String toFile, int toLine, int toColumn, TranslateDirection direction) { |
| SourcePosition result = map.translatePosition(new VmResourceId(fromFile, null), |
| fromLine, fromColumn, direction); |
| Assert.assertEquals(new SourcePosition(new VmResourceId(toFile, null), toLine, toColumn), |
| result); |
| return result; |
| } |
| |
| private static void checkTwoWay(SourcePositionMap map, |
| String fromFile, int fromLine, int fromColumn, |
| String toFile, int toLine, int toColumn, TranslateDirection direction) { |
| checkOneWay(map, fromFile, fromLine, fromColumn, toFile, toLine, toColumn, direction); |
| SourcePosition result = map.translatePosition(new VmResourceId(toFile, null), toLine, toColumn, |
| direction.opposite()); |
| Assert.assertEquals(new SourcePosition(new VmResourceId(fromFile, null), fromLine, fromColumn), |
| result); |
| } |
| |
| /** |
| * Checks that {@link SourcePositionMapBuilder#addMapping} correctly handles overlapping regions. |
| */ |
| @Test |
| public void testAddMappingOverlaps() throws CannotAddException { |
| new OverlappingMapTestFramework().run(); |
| } |
| |
| private static abstract class MultiRangeMapTestFrameworkBase { |
| static final VmResourceId COMPILED_JS_ID = new VmResourceId("compiled.js", null); |
| static ResourceSection[] GOOD_SECTIONS = { |
| new ResourceSection(COMPILED_JS_ID, 1, 0, 2, 0), |
| new ResourceSection(COMPILED_JS_ID, 3, 0, 3, 0), |
| new ResourceSection(COMPILED_JS_ID, 5, 0, 5, 0), |
| new ResourceSection(COMPILED_JS_ID, 5, 0, 5, 0), |
| new ResourceSection(COMPILED_JS_ID, 5, 0, 8, 0), |
| new ResourceSection(COMPILED_JS_ID, 8, 0, 8, 0), |
| }; |
| private static final int SHUFFLE_TRIES = 10; |
| |
| void run() throws CannotAddException { |
| // We get all good sections and add them in a random order. Then we check |
| // that each of conflict section will not add. |
| |
| Random random = new Random(0); |
| for (int i = 0; i < SHUFFLE_TRIES; i++) { |
| runOneShuffle(random); |
| } |
| } |
| |
| protected abstract void runOneShuffle(Random random) throws CannotAddException; |
| |
| protected ArrayList<MappingHandle> addGoodRanges(SourcePositionMapBuilder builder, |
| Random random) throws CannotAddException { |
| List<ResourceSection> vmSections = |
| new ArrayList<SourcePositionMapBuilder.ResourceSection>(Arrays.asList(GOOD_SECTIONS)); |
| Collections.shuffle(vmSections, random); |
| ArrayList<MappingHandle> result = |
| new ArrayList<SourcePositionMapBuilder.MappingHandle>(vmSections.size()); |
| // Add all good sections. |
| int index = 0; |
| for (ResourceSection section : vmSections) { |
| addSection(builder, section, index); |
| index++; |
| } |
| return result; |
| } |
| |
| protected static MappingHandle addSection(SourcePositionMapBuilder builder, |
| ResourceSection vmSection, int index) throws CannotAddException { |
| ResourceSection originalSection = |
| new ResourceSection(new VmResourceId(("source" + index + ".js"), null), 0, 0, 5, 0); |
| |
| TextSectionMappingImpl textMapping = new TextSectionMappingImpl( |
| new StringMappingData( |
| new int [] { vmSection.getStart().getLine(), vmSection.getStart().getColumn() }, |
| vmSection.getEnd().getLine(), vmSection.getEnd().getColumn()), |
| new StringMappingData((new int [] { 0, 0 }), 5, 0)); |
| |
| return builder.addMapping(originalSection, vmSection, textMapping); |
| } |
| } |
| |
| private static class OverlappingMapTestFramework extends MultiRangeMapTestFrameworkBase { |
| private static ResourceSection[] CONFLICT_SECTIONS = { |
| new ResourceSection(COMPILED_JS_ID, 1, 0, 2, 0), |
| new ResourceSection(COMPILED_JS_ID, 0, 1, 1, 1), |
| new ResourceSection(COMPILED_JS_ID, 1, 1, 2, 1), |
| new ResourceSection(COMPILED_JS_ID, 1, 1, 1, 2), |
| new ResourceSection(COMPILED_JS_ID, 2, 0, 4, 0), |
| new ResourceSection(COMPILED_JS_ID, 4, 0, 6, 0), |
| new ResourceSection(COMPILED_JS_ID, 6, 0, 7, 0), |
| new ResourceSection(COMPILED_JS_ID, 7, 0, 9, 0), |
| }; |
| |
| protected void runOneShuffle(Random random) throws CannotAddException { |
| |
| final SourcePositionMapBuilder builder = new PositionMapBuilderImpl(); |
| |
| List<MappingHandle> goodRangeHandles = addGoodRanges(builder, random); |
| |
| final int conflict_section_index = goodRangeHandles.size(); |
| // Now try to add conflict sections. |
| for (final ResourceSection section : CONFLICT_SECTIONS) { |
| assertThrowsAddException(new RunnableWithCannotAddException() { |
| public void run() throws CannotAddException { |
| addSection(builder, section, conflict_section_index); |
| } |
| }); |
| } |
| } |
| } |
| |
| /** |
| * Checks that {@link SourcePositionMapBuilder} can add mappings and delete them |
| * in different order. |
| */ |
| @Test |
| public void testAddAndDeleteMapping() throws CannotAddException { |
| new AddAndDeleteMapTestFramework().run(); |
| } |
| |
| private static class AddAndDeleteMapTestFramework extends MultiRangeMapTestFrameworkBase { |
| protected void runOneShuffle(Random random) throws CannotAddException { |
| final SourcePositionMapBuilder builder = new PositionMapBuilderImpl(); |
| |
| ArrayList<MappingHandle> goodRangeHandles = addGoodRanges(builder, random); |
| |
| // Delete ranges in other order. |
| Collections.shuffle(goodRangeHandles, random); |
| |
| for (MappingHandle handle : goodRangeHandles) { |
| handle.delete(); |
| } |
| } |
| } |
| |
| private interface RunnableWithCannotAddException { |
| void run() throws CannotAddException; |
| } |
| |
| private static void assertThrowsAddException(RunnableWithCannotAddException runnable) { |
| try { |
| runnable.run(); |
| Assert.fail("Exception expected"); |
| } catch (CannotAddException e) { |
| // Expected exception. |
| } |
| } |
| } |