/* * Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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.gwt.core.ext.soyc; import com.google.gwt.dev.jjs.SourceOrigin; import com.google.gwt.thirdparty.debugging.sourcemap.FilePosition; import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapConsumerV3; import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapConsumerV3.EntryVisitor; import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapGeneratorV3; import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapParseException; import com.google.gwt.thirdparty.guava.common.base.Joiner; import junit.framework.TestCase; import java.io.IOException; /** * Verifies that we consolidate source mappings. */ public class SourceMappingWriterTest extends TestCase { private static final String JAVA_FILENAME = "Hello.java"; private SourceMapGeneratorV3 generator; private SourceMappingWriter writer; private String javascript; @Override protected void setUp() throws Exception { super.setUp(); generator = new SourceMapGeneratorV3(); writer = new SourceMappingWriter(generator); } public void testNoMappings() throws Exception { checkMappings(); } public void testOneMapping() throws Exception { javascript = "foo.bar"; addMapping("foo", 123); checkMappings("0:0-3 -> 123"); } public void testMappingsToSeparateLines() throws Exception { javascript = "foo.bar"; addMapping("foo", 123); addMapping(".bar", 456); checkMappings( "0:0-3 -> 123", "0:3-7 -> 456" ); } public void testMappingsToSameLine() throws Exception { javascript = "foo.bar"; addMapping("foo", 123); addMapping(".bar", 123); checkMappings( "0:0-7 -> 123" ); } public void testOverlappingMappings() throws Exception { javascript = "foo.bar"; addMapping("foo.bar", 123); addMapping("foo", 123); addMapping("bar", 123); checkMappings( "0:0-7 -> 123" ); } public void testInlinedExpression() throws Exception { javascript = "foo.bar.baz"; addMapping("foo.bar.baz", 123); addMapping(".bar", 456); checkMappings( "0:0-3 -> 123", "0:3-7 -> 456", "0:7-11 -> 123" ); } private void addMapping(String substring, int javaStartLine) { SourceOrigin sourceInfo = SourceOrigin.create(javaStartLine, JAVA_FILENAME); writer.addMapping(findRange(substring, sourceInfo), null); } /** * Returns the appropriate range for the given substring of the "javascript" field. */ private Range findRange(String substring, SourceOrigin sourceInfo) { assertFalse("multiline strings not implemented", substring.contains("\n")); int startPos = javascript.indexOf(substring); assertTrue("can't find javascript substring: " + substring, startPos >= 0); int startLine = 0; int startChar = 0; for (int c = 0; c < startPos; c++) { if (javascript.charAt(c) == '\n') { startLine++; startChar = 0; } else { startChar++; } } return new Range(startPos, startPos + substring.length(), startLine, startChar, startLine, startChar + substring.length(), sourceInfo); } private void checkMappings(String... lines) throws IOException, SourceMapParseException { String expected = "Mappings:\n"; if (lines.length > 0) { expected += Joiner.on("\n").join(lines) + "\n"; } assertEquals(expected, dumpMappings()); } private String dumpMappings() throws IOException, SourceMapParseException { writer.flush(); // Workaround: SourceMapConsumerV3 omits the last mapping. Add one more mapping until // this is fixed: https://code.google.com/p/closure-compiler/issues/detail?id=1311 generator.addMapping("Dummy", null, new FilePosition(0, 0), new FilePosition(99, 0), new FilePosition(99, 1)); StringBuilder generated = new StringBuilder(); generator.appendTo(generated, "test"); SourceMapConsumerV3 consumer = new SourceMapConsumerV3(); consumer.parse(generated.toString()); final StringBuilder out = new StringBuilder(); out.append("Mappings:\n"); consumer.visitMappings(new EntryVisitor() { @Override public void visit(String javaFile, String javaName, FilePosition javaStart, FilePosition jsStart, FilePosition jsEnd) { if (javaFile.equals("Dummy")) { return; // Ignore dummy mapping (in case the bug is fixed.) } // JavaScript range out.append(jsStart.getLine()); out.append(":"); out.append(jsStart.getColumn()); out.append("-"); if (jsEnd.getLine() != jsStart.getLine()) { out.append(jsEnd.getLine()); out.append(":"); } out.append(jsEnd.getColumn()); out.append(" -> "); // Java target if (!javaFile.equals(JAVA_FILENAME)) { out.append(javaFile); out.append(":"); } // print Java line using one-based line numbers to avoid confusion out.append(javaStart.getLine() + 1); if (javaStart.getColumn() != 0) { out.append(":"); out.append(javaStart.getColumn()); } if (javaName != null) { out.append(" ("); out.append(javaName); out.append(")"); } out.append("\n"); } }); return out.toString(); } }