Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extend CompilerBasedTransformer to CoverageInstrumenter
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170706158
- Loading branch information
1 parent
66262ea
commit de10f16
Showing
3 changed files
with
184 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
src/com/google/javascript/jscomp/bundle/CoverageInstrumenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright 2017 The Closure Compiler Authors. | ||
* | ||
* 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.javascript.jscomp.bundle; | ||
|
||
import com.google.common.annotations.GwtIncompatible; | ||
import com.google.errorprone.annotations.Immutable; | ||
import com.google.javascript.jscomp.CompilerOptions; | ||
import com.google.javascript.jscomp.Result; | ||
import com.google.javascript.jscomp.VariableRenamingPolicy; | ||
import java.util.Optional; | ||
|
||
/** | ||
* A source transformer for instrmenting code for coverage data collection. | ||
*/ | ||
@GwtIncompatible | ||
@Immutable | ||
public class CoverageInstrumenter extends CompilerBasedTransformer { | ||
|
||
public CoverageInstrumenter(CompilerBasedTransformer.CompilerSupplier compilerSupplier) { | ||
super(compilerSupplier); | ||
} | ||
/** | ||
* Supply options for coverage. | ||
*/ | ||
public static class CompilerSupplier extends CompilerBasedTransformer.CompilerSupplier { | ||
@Override | ||
protected void setOptions(CompilerOptions options) { | ||
options.coalesceVariableNames = false; | ||
options.setLanguageOut(CompilerOptions.LanguageMode.ECMASCRIPT5); | ||
// The next two options together sum to the deprecated ECMASCRIPT6, and | ||
// mimic historical behavior of this class. | ||
options.setLanguageIn(CompilerOptions.LanguageMode.ECMASCRIPT_2015); | ||
options.setStrictModeInput(false); | ||
options.setShadowVariables(false); | ||
// Setting the path to any non-null value will trigger source map generation. | ||
// CompilerBasedTransformer attachs the sourcemap to the result. | ||
options.setSourceMapOutputPath("/dev/null"); | ||
options.setVariableRenaming(VariableRenamingPolicy.OFF); | ||
options.instrumentForCoverage = true; | ||
options.setInstrumentForCoverageOnly(true); | ||
} | ||
|
||
@Override | ||
public boolean transformed(Result result) { | ||
return true; | ||
} | ||
} | ||
|
||
@Override | ||
public Optional<String> getRuntime() { | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public String getTranformationName() { | ||
return "Coverage Instrumentation"; | ||
} | ||
|
||
public static CoverageInstrumenter.CompilerSupplier compilerSupplier() { | ||
return new CoverageInstrumenter.CompilerSupplier(); | ||
} | ||
|
||
public static final CoverageInstrumenter INSTRUMENTER = | ||
new CoverageInstrumenter(CoverageInstrumenter.compilerSupplier()); | ||
} |
100 changes: 100 additions & 0 deletions
100
test/com/google/javascript/jscomp/bundle/CoverageInstrumenterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* | ||
* Copyright 2017 The Closure Compiler Authors. | ||
* | ||
* 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.javascript.jscomp.bundle; | ||
|
||
import static com.google.common.truth.Truth.assertThat; | ||
import static org.mockito.Answers.RETURNS_SMART_NULLS; | ||
import static org.mockito.Mockito.when; | ||
|
||
import com.google.common.annotations.GwtIncompatible; | ||
import com.google.common.base.Joiner; | ||
import com.google.javascript.jscomp.JSError; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import junit.framework.TestCase; | ||
import org.mockito.Mock; | ||
import org.mockito.MockitoAnnotations; | ||
|
||
/** Tests for {@link CoverageInstrumenter}. */ | ||
@GwtIncompatible | ||
|
||
public final class CoverageInstrumenterTest extends TestCase { | ||
|
||
private Source.Transformer instrumenter; | ||
private CoverageInstrumenter.CompilerSupplier compiler; | ||
|
||
@Mock(answer = RETURNS_SMART_NULLS) | ||
CoverageInstrumenter.CompilerSupplier mockCompiler; | ||
|
||
private static final Path FOO_JS = Paths.get("foo.js"); | ||
private static final Path SOURCE_JS = Paths.get("source.js"); | ||
private static final JSError[] NO_ERRORS = new JSError[] {}; | ||
|
||
@Override | ||
public void setUp() { | ||
MockitoAnnotations.initMocks(this); | ||
instrumenter = new CoverageInstrumenter(mockCompiler); | ||
compiler = CoverageInstrumenter.compilerSupplier(); | ||
} | ||
|
||
// Tests for CoverageInstrumenter | ||
|
||
public void testCoverageInstrumenter_instrument() { | ||
when(mockCompiler.compile(FOO_JS, "bar")) | ||
.thenReturn(new CoverageInstrumenter.CompileResult("result", NO_ERRORS, true, "srcmap")); | ||
assertThat(instrumenter.transform(source(FOO_JS, "bar"))) | ||
.isEqualTo( | ||
Source.builder() | ||
.setPath(FOO_JS) | ||
.setOriginalCode("bar") | ||
.setCode("result") | ||
.setSourceMap("srcmap") | ||
.build()); | ||
} | ||
|
||
public void testCoverageInstrumenter_noInstrumentation() { | ||
when(mockCompiler.compile(FOO_JS, "bar")) | ||
.thenReturn(new CoverageInstrumenter.CompileResult("result", NO_ERRORS, false, "srcmap")); | ||
assertThat(instrumenter.transform(source(FOO_JS, "bar"))).isEqualTo(source(FOO_JS, "bar")); | ||
} | ||
|
||
private static Source source(Path path, String code) { | ||
return Source.builder().setPath(path).setCode(code).build(); | ||
} | ||
|
||
// Tests for CompilerSupplier | ||
|
||
public void testCompilerSupplier_instruments() { | ||
CoverageInstrumenter.CompileResult result = compiler.compile(SOURCE_JS, "var x = 42;"); | ||
String[] expected = new String[]{ | ||
"if(!self.window){self.window=self;self.window.top=self}", | ||
"var __jscov=window.top.__jscov||", | ||
"(window.top.__jscov={fileNames:[],instrumentedLines:[],executedLines:[]});", | ||
"var JSCompiler_lcov_data_source_js=[];", | ||
"__jscov.executedLines.push(JSCompiler_lcov_data_source_js);", | ||
"__jscov.instrumentedLines.push(\"01\");", | ||
"__jscov.fileNames.push(\"source.js\");", | ||
"JSCompiler_lcov_data_source_js[0]=true;var x=42;"}; | ||
|
||
assertThat(result.source).isEqualTo(Joiner.on("").join(expected)); | ||
assertThat(result.errors).isEmpty(); | ||
assertThat(result.transformed).isTrue(); | ||
assertThat(result.sourceMap) | ||
.contains("\"mappings\":\"AAAA,GAAI,CAAC,IAAA,OAAL,CAAkB,CAAE,IAAA,OAAA,CAAc,IAAM,"); | ||
} | ||
|
||
} |