/* * Copyright 2009 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.jstestdriver.coverage; import java.util.Iterator; import java.util.List; import junit.framework.TestCase; import com.google.common.collect.Lists; public class CodeInstrumentorTest extends TestCase { public void testSourceFileLineCoverageDecoration() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2]);LCOV_HASH[1]++;").code(" var a = 1;") .instrument("LCOV_HASH[2]++;").code(" var b = 2;") .assertCoverage(); } //TODO(corysmith): Figure out how to fix this test. /*public void testSourceFileLineCommentDecoration() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2]);").code("//foo") .noInstrument().code("// bar") .assertCoverage(); }*/ public void testDoNotDecorateStatementsAcrossMultipleLines() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 5]);LCOV_HASH[1]++;").code("var a = 0;") .instrument("LCOV_HASH[2]++;").code("a = true") .noInstrument().code("? 1") .noInstrument().code(": 2;") .instrument("LCOV_HASH[5]++;").code(" a = 3;") .assertCoverage(); } public void testDecorateIfStatement() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 3, 5, 7]);LCOV_HASH[1]++;") .code("var a = 0;") .instrument("LCOV_HASH[2]++;").code("if (a) {") .instrument("LCOV_HASH[3]++;").code(" a = 1;") .noInstrument().code( "}else{") .instrument("LCOV_HASH[5]++;").code(" a = 2;") .noInstrument().code( "}") .instrument("LCOV_HASH[7]++;").code("a = 3;") .assertCoverage(); } public void testDecorateNakedIfWithMultilineStatement() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 3, 5]);LCOV_HASH[1]++;") .code("var a = 0;") .instrument("LCOV_HASH[2]++;") .code("if (a)") .instrument("{LCOV_HASH[3]++;").code("hello(") .noInstrument() .code("6);", "6);}") .instrument("LCOV_HASH[5]++;") .code("a = 3;") .assertCoverage(); } public void testDecorateFunctionStatement() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 3, 5]);LCOV_HASH[1]++;") .code(" var a = 0;") .instrument("LCOV_HASH[2]++;").code("function foo (a) {") .instrument("LCOV_HASH[3]++;").code(" a = 1;") .noInstrument() .code("}") .instrument("LCOV_HASH[5]++;").code(" a = 3;") .assertCoverage(); } public void testDecorateWhileStatement() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 3, 5]);LCOV_HASH[1]++;") .code(" var a = 0;") .instrument("LCOV_HASH[2]++;").code("while (a) {") .instrument("LCOV_HASH[3]++;").code(" a = 1;") .noInstrument() .code("}") .instrument("LCOV_HASH[5]++;").code("a = 3;") .assertCoverage(); } public void testDecorateForStatement() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 5, 7]);LCOV_HASH[1]++;") .code("var a = 0;") .instrument("LCOV_HASH[2]++;").code("for (") .noInstrument() .code(" var a = 1;") .noInstrument() .code(" a < 20; a++){") .instrument("LCOV_HASH[5]++;").code(" a = 1;") .noInstrument() .code("}") .instrument("LCOV_HASH[7]++;").code("a = 3;") .assertCoverage(); } public void testDecorateNakedForStatement() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 5, 6]);LCOV_HASH[1]++;") .code("var a = 0;") .instrument("LCOV_HASH[2]++;").code("for (") .noInstrument() .code(" var a = 1;") .noInstrument() .code(" a < 20; a++)") .instrument("{LCOV_HASH[5]++;").code("a = 1;", "a = 1;}") .instrument("LCOV_HASH[6]++;").code("a = 3;") .assertCoverage(); } public void testDecorateNakedIfStatement() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 3, 5, 6]);LCOV_HASH[1]++;") .code("var a = 0;") .instrument("LCOV_HASH[2]++;") .code("if (a)") .instrument("{LCOV_HASH[3]++;").code(" a = 1;", " a = 1;}") .noInstrument() .code("else") .instrument("{LCOV_HASH[5]++;").code(" a = 2;", " a = 2;}") .instrument("LCOV_HASH[6]++;") .code("a = 3;") .assertCoverage(); } public void testDecorateNakedNestedIfStatements() throws Exception { new CoverageAsserter() .instrument("LCOV_HASH=LCOV.initNoop('FILE',0,[1, 2, 3, 4, 5]);LCOV_HASH[1]++;") .code("var a = 0;") .instrument("LCOV_HASH[2]++;") .code("if (a)") .instrument("{LCOV_HASH[3]++;").code(" if(a)") .instrument("{LCOV_HASH[4]++;").code(" a = 2;", " a = 2;}}") .instrument("LCOV_HASH[5]++;") .code("a = 3;") .assertCoverage(); } private static class CoverageAsserter { List<Instrumentation> instruments = Lists.newLinkedList(); List<CodeLine> source = Lists.newLinkedList(); String filePath = "filename.js"; CoverageNameMapper mapper = new CoverageNameMapper(); String hash = Integer.toString( Math.abs( String.valueOf( mapper.map(filePath)).hashCode()), Character.MAX_RADIX); public CoverageAsserter instrument(String instrument) { instruments.add(new LcovInstrumentation(instrument.replaceAll("LCOV_HASH", "LCOV_" + hash).replaceAll("'FILE'", String.valueOf(mapper.map(filePath))))); return this; } public CoverageAsserter noInstrument() { instruments.add(new NoInstrumentation()); return this; } public CoverageAsserter code(String code) { source.add(new CodeLine(code)); return this; } public CoverageAsserter code(String original, String expected) { source.add(new CodeLine(original, expected)); return this; } public void assertCoverage() { StringBuilder sourceCode = new StringBuilder(); for (CodeLine line : source) { sourceCode.append(line.getSource()); } Instrumentor decorator = new CodeInstrumentor(mapper); Iterator<Instrumentation> instrumentsIterator = instruments.iterator(); Iterator<CodeLine> sourceIterator = source.iterator(); InstrumentedCode decorated = decorator.instrument( new Code(filePath, sourceCode.toString())); for(String actual : decorated.getInstrumentedCode().split("\n")) { instrumentsIterator.next().assertLine(actual); sourceIterator.next().assertLine(actual); } } } private static class CodeLine { private final String original; private final String expected; public CodeLine(String code) { expected = code; this.original = code; } public CodeLine(String original, String expected) { this.original = original; this.expected = expected; } public void assertLine(String actual) { assertTrue("[" + actual + "] does not contain [" + expected +"]", actual.contains(expected.trim())); } public String getSource() { return original + "\n"; } } private static interface Instrumentation { public void assertLine(String actual); } private static class LcovInstrumentation implements Instrumentation{ private final String instrument; public LcovInstrumentation(String instrument) { this.instrument = instrument; } public void assertLine(String actual) { assertTrue("["+actual + "] does not contain [" + instrument + "]", actual.contains(instrument)); } } private static class NoInstrumentation implements Instrumentation { public void assertLine(String actual) { assertFalse(actual + " does contain LCOV_", actual.contains("LCOV_")); } } }