diff --git a/src/com/google/common/css/compiler/passes/DefaultGssSourceMapGenerator.java b/src/com/google/common/css/compiler/passes/DefaultGssSourceMapGenerator.java index 65472046..4412e1bb 100644 --- a/src/com/google/common/css/compiler/passes/DefaultGssSourceMapGenerator.java +++ b/src/com/google/common/css/compiler/passes/DefaultGssSourceMapGenerator.java @@ -154,7 +154,10 @@ public void startSourceMapping(CssNode node, int startLine, int startCharIndex) public void endSourceMapping(CssNode node, int endLine, int endCharIndex) { Preconditions.checkState(node != null); Preconditions.checkState(endLine >= 0); - Preconditions.checkState(endCharIndex >= 0); + // -1 when a node contributes no content at the start of the buffer, + // as when a CssImportBlockNode is encountered, and there is no + // copyright comment. + Preconditions.checkState(endCharIndex >= -1); if (!mappings.isEmpty() && mappings.peek().node == node) { Mapping mapping = mappings.pop(); mapping.end = new FilePosition(endLine, endCharIndex); diff --git a/src/com/google/common/css/compiler/passes/GssSourceMapGenerator.java b/src/com/google/common/css/compiler/passes/GssSourceMapGenerator.java index ba2604b2..6843a724 100644 --- a/src/com/google/common/css/compiler/passes/GssSourceMapGenerator.java +++ b/src/com/google/common/css/compiler/passes/GssSourceMapGenerator.java @@ -51,6 +51,8 @@ public interface GssSourceMapGenerator { * @param node the {@link CssNode} to be processed * @param endLine the last character's line number when it ends writing output * @param endCharIndex the last character's character index when it ends writing output + * or one less than the corresponding {@link #startSourceMapping startCharIndex} if + * a source mapping is empty. */ public void endSourceMapping(CssNode node, int endLine, int endCharIndex); diff --git a/tests/com/google/common/css/compiler/commandline/ClosureCommandLineCompilerTest.java b/tests/com/google/common/css/compiler/commandline/ClosureCommandLineCompilerTest.java index 176712dd..43309bfe 100644 --- a/tests/com/google/common/css/compiler/commandline/ClosureCommandLineCompilerTest.java +++ b/tests/com/google/common/css/compiler/commandline/ClosureCommandLineCompilerTest.java @@ -21,9 +21,12 @@ import com.google.common.css.SourceCode; import com.google.common.css.compiler.ast.ErrorManager; import com.google.common.css.compiler.ast.testing.NewFunctionalTestBase; +import com.google.common.io.Files; import junit.framework.TestCase; +import java.io.File; + public class ClosureCommandLineCompilerTest extends TestCase { static final ExitCodeHandler EXIT_CODE_HANDLER = @@ -64,4 +67,30 @@ public void testAllowDefPropagationDefaultsToTrue() throws Exception { JobDescription jobDescription = flags.createJobDescription(); assertTrue(jobDescription.allowDefPropagation); } + + + public void testEmptyImportBlocks() throws Exception { + // See b/29995881 + ErrorManager errorManager = new NewFunctionalTestBase.TestErrorManager(new String[0]); + + JobDescription job = new JobDescriptionBuilder() + .addInput(new SourceCode("main.css", "@import 'common.css';")) + .addInput(new SourceCode("common.css", "/* common */")) + .setOptimizeStrategy(JobDescription.OptimizeStrategy.SAFE) + .setCreateSourceMap(true) + .getJobDescription(); + + File outputDir = Files.createTempDir(); + File sourceMapFile = new File(outputDir, "sourceMap"); + + String compiledCss = new ClosureCommandLineCompiler( + job, EXIT_CODE_HANDLER, errorManager) + .execute(null /*renameFile*/, sourceMapFile); + + // The symptom was an IllegalStateException trapped by the compiler that + // resulted in the exit handler being called which causes fail(...), + // so if control reaches here, we're ok. + + assertNotNull(compiledCss); + } }