/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.st.test;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Source;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import com.oracle.truffle.st.Coverage;
import com.oracle.truffle.st.SimpleCoverageInstrument;
public class SimpleCoverageInstrumentTest {
private static final String JS_SOURCE = "var N = 2000;\n" +
"var EXPECTED = 17393;\n" +
"\n" +
"function Natural() {\n" +
" x = 2;\n" +
" return {\n" +
" 'next' : function() { return x++; }\n" +
" };\n" +
"}\n" +
"\n" +
"function Filter(number, filter) {\n" +
" var self = this;\n" +
" this.number = number;\n" +
" this.filter = filter;\n" +
" this.accept = function(n) {\n" +
" var filter = self;\n" +
" for (;;) {\n" +
" if (n % filter.number === 0) {\n" +
" return false;\n" +
" }\n" +
" filter = filter.filter;\n" +
" if (filter === null) {\n" +
" break;\n" +
" }\n" +
" }\n" +
" return true;\n" +
" };\n" +
" return this;\n" +
"}\n" +
"\n" +
"function Primes(natural) {\n" +
" var self = this;\n" +
" this.natural = natural;\n" +
" this.filter = null;\n" +
" this.next = function() {\n" +
" for (;;) {\n" +
" var n = self.natural.next();\n" +
" if (self.filter === null || self.filter.accept(n)) {\n" +
" self.filter = new Filter(n, self.filter);\n" +
" return n;\n" +
" }\n" +
" }\n" +
" };\n" +
"}\n" +
"\n" +
"var holdsAFunctionThatIsNeverCalled = function(natural) {\n" +
" var self = this;\n" +
" this.natural = natural;\n" +
" this.filter = null;\n" +
" this.next = function() {\n" +
" for (;;) {\n" +
" var n = self.natural.next();\n" +
" if (self.filter === null || self.filter.accept(n)) {\n" +
" self.filter = new Filter(n, self.filter);\n" +
" return n;\n" +
" }\n" +
" }\n" +
" };\n" +
"}\n" +
"\n" +
"var holdsAFunctionThatIsNeverCalledOneLine = function() {return null;}\n" +
"\n" +
"function primesMain() {\n" +
" var primes = new Primes(Natural());\n" +
" var primArray = [];\n" +
" for (var i=0;i<=N;i++) { primArray.push(primes.next()); }\n" +
" if (primArray[N] != EXPECTED) { throw new Error('wrong prime found: ' + primArray[N]); }\n" +
"}\n" +
"primesMain();\n";
@Test
public void exampleJSTest() throws IOException {
// This test only makes sense if JS is available.
Assume.assumeTrue(Engine.create().getLanguages().containsKey("js"));
// This is how we can create a context with our tool enabled if we are embeddined in java
try (Context context = Context.newBuilder("js").option(SimpleCoverageInstrument.ID, "true").option(SimpleCoverageInstrument.ID + ".PrintCoverage", "false").build()) {
Source source = Source.newBuilder("js", JS_SOURCE, "main").build();
context.eval(source);
assertJSCorrect(context);
}
}
// NOTE: This lookup mechanism used in this method does not work in normal deployments
// due to Truffles class path issolation. Services can be looked up by other
// instruments, but not by the embedder. We can do this in the tests because the
// class path issolation is disabled in the pom.xml file by adding -XX:-UseJVMCIClassLoader to
// the command line.
// This command line flag should never be used in production.
private static void assertJSCorrect(final Context context) {
// We can lookup services exported by the instrument, in our case this is
// the instrument itself but it does not have to be.
SimpleCoverageInstrument coverageInstrument = context.getEngine().getInstruments().get(SimpleCoverageInstrument.ID).lookup(SimpleCoverageInstrument.class);
// We then use the looked up service to assert that it behaves as expected, just like in any
// other test.
Map<com.oracle.truffle.api.source.Source, Coverage> coverageMap = coverageInstrument.getCoverageMap();
Assert.assertEquals(1, coverageMap.size());
coverageMap.forEach((com.oracle.truffle.api.source.Source s, Coverage v) -> {
Set<Integer> notYetCoveredLineNumbers = coverageInstrument.nonCoveredLineNumbers(s);
Object[] expected = new Integer[]{47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 61, 67};
Assert.assertArrayEquals(expected, notYetCoveredLineNumbers.stream().sorted().toArray());
});
}
private static final String SL_SOURCE = "\n" +
"function neverCalled() {\n" +
" x = 5;\n" +
" y = 9;\n" +
" return x + 5;\n" +
"}\n" +
"function isCalled() {\n" +
" return 5 + 5;\n" +
"}\n" +
"function main() { \n" +
" 10 + isCalled(); \n" +
"}\n";
// Similar test using simple language
@Test
public void exampleSLTest() throws IOException {
// This test only makes sense if SL is available.
Assume.assumeTrue(Engine.create().getLanguages().containsKey("sl"));
// This is how we can create a context with our tool enabled if we are embeddined in java
try (Context context = Context.newBuilder("sl").option(SimpleCoverageInstrument.ID, "true").option(SimpleCoverageInstrument.ID + ".PrintCoverage", "false").build()) {
Source source = Source.newBuilder("sl", SL_SOURCE, "main").build();
context.eval(source);
// We can lookup services exported by the instrument, in our case this is
// the instrument itself but it does not have to be.
SimpleCoverageInstrument coverageInstrument = context.getEngine().getInstruments().get(SimpleCoverageInstrument.ID).lookup(SimpleCoverageInstrument.class);
// We then use the looked up service to assert that it behaves as expected, just like in
// any other test.
Map<com.oracle.truffle.api.source.Source, Coverage> coverageMap = coverageInstrument.getCoverageMap();
Assert.assertEquals(1, coverageMap.size());
coverageMap.forEach((com.oracle.truffle.api.source.Source s, Coverage v) -> {
Set<Integer> notYetCoveredLineNumbers = coverageInstrument.nonCoveredLineNumbers(s);
Object[] expected = new Integer[]{3, 4, 5};
Assert.assertArrayEquals(expected, notYetCoveredLineNumbers.stream().sorted().toArray());
});
}
}
}