Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run unit tests with truffle-compiler #8467

Merged
merged 50 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
af6d619
logging-service-logback is JPMS module
Akirathan Dec 5, 2023
8f6157e
runtime uses truffle-compiler in tests
Akirathan Dec 5, 2023
2f7521e
Revert "logging-service-logback is JPMS module"
Akirathan Dec 5, 2023
308f8e8
Refactor Test/options to a separate task
Akirathan Dec 5, 2023
5dc8572
Provide a default logback-test logging config for tests
Akirathan Dec 5, 2023
b99c4a9
Do not add runtime-with-instruments to --patch-module
Akirathan Dec 5, 2023
9357291
Refactor mutable ListBuffer to List
Akirathan Dec 5, 2023
d3c5048
Introduce runtime-fat-jar project.
Akirathan Dec 6, 2023
3e0c531
compileModuleInfo task does not copy anything unless necessary
Akirathan Dec 7, 2023
ec95060
Merge branch 'develop' into wip/akirathan/8374-compiler-to-tests
Akirathan Dec 7, 2023
1c516be
JPMSUtils.filterModules filters only distinct modules
Akirathan Dec 7, 2023
0dd0056
Make sure that all GraalVM modules (including truffle langs) are in -…
Akirathan Dec 7, 2023
4a9c962
Create new module org.enso.interpreter.test
Akirathan Dec 8, 2023
81ec91e
module-info.java is not compiled every time
Akirathan Dec 8, 2023
ea70d95
Revert "Create new module org.enso.interpreter.test"
Akirathan Dec 11, 2023
1d42a25
Add skeleton of JPMSPlugin auto plugin.
Akirathan Dec 12, 2023
2af02fb
Add runtime-test-instruments project.
Akirathan Dec 12, 2023
95381be
Fix VectorTests in runtime
Akirathan Dec 12, 2023
6997cff
Fix the rest of runtime tests
Akirathan Dec 12, 2023
ffd95c4
runtime-with-instruments/test run with truffle-compiler
Akirathan Dec 12, 2023
acb27fc
fmt
Akirathan Dec 12, 2023
78c40a1
Fix some sbt issues with lazy val settings
Akirathan Dec 12, 2023
b98526e
Use service provider instead of reflection in runtime-test-instruments
Akirathan Dec 13, 2023
07f5fa4
Port NodeCountingTestInstrument into runtime-test-instruments
Akirathan Dec 14, 2023
05b221a
Merge runtime-with-instruments project into runtime/Test
Akirathan Dec 14, 2023
b66b3d7
Merge runtime-with-polyglot project into runtime/Test
Akirathan Dec 14, 2023
c9bf46f
Run runtime/test without -ea
Akirathan Dec 14, 2023
b68412e
fmt
Akirathan Dec 14, 2023
a4a82ab
Merge branch 'develop' into wip/akirathan/8374-compiler-to-tests
Akirathan Dec 14, 2023
111cda6
Include insight tool in runtime/Test
Akirathan Dec 15, 2023
256fb9c
withDebug command has better error message
Akirathan Dec 15, 2023
efa56f8
Remove BigNumberTest.averageOfMixedArrayOverDouble.
Akirathan Dec 15, 2023
b64d2b4
Fix context dispose in MetaObjectTest
Akirathan Dec 15, 2023
c3264d7
project-manager/test runs with truffle-compiler
Akirathan Dec 15, 2023
303d272
language-server/test runs with truffle-compiler
Akirathan Dec 15, 2023
0ac7249
JPMSUtils.compileModuleInfo takes an extraModulePath optional argument
Akirathan Dec 15, 2023
283fecb
Merge branch 'develop' into wip/akirathan/8374-compiler-to-tests
Akirathan Dec 15, 2023
b9e1c3d
Small fixes after merge
Akirathan Dec 15, 2023
9d5071f
langauge-server/test uses enso TestLogProvider
Akirathan Dec 15, 2023
8c90c5e
Add necessary modules on modulePath to language-server/test
Akirathan Dec 15, 2023
1ce3069
Add --patch-module option to language-server/test
Akirathan Dec 18, 2023
a342562
runtime/test uses Enso TestLogProvider
Akirathan Dec 18, 2023
3d097ee
Fix DebuggingEnsoTest.
Akirathan Dec 18, 2023
9ecbd68
Make sure runtime/test is run with -ea
Akirathan Dec 18, 2023
e8846cc
Make sure runtime/test is run with -ea
Akirathan Dec 18, 2023
e580ed9
Remove dependency on com.sandinh.sbt-java-module-info plugin
Akirathan Dec 18, 2023
e7171e9
CLeanup some unused methods
Akirathan Dec 18, 2023
e8ca9a9
fmt
Akirathan Dec 18, 2023
ef71ecf
project-manager/test uses TestLogProvider
Akirathan Dec 18, 2023
9820863
Remove unused logback-test.xml resource
Akirathan Dec 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
511 changes: 439 additions & 72 deletions build.sbt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module org.enso.runtime.test {
requires org.graalvm.truffle;

exports org.enso.interpreter.test.instruments;

provides com.oracle.truffle.api.instrumentation.provider.TruffleInstrumentProvider with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate module & module info for test only instruments is a good idea.

org.enso.interpreter.test.instruments.CodeIdsTestInstrumentProvider,
org.enso.interpreter.test.instruments.CodeLocationsTestInstrumentProvider;
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.enso.interpreter.test;
package org.enso.interpreter.test.instruments;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.*;
import com.oracle.truffle.api.nodes.Node;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.control.TailCallException;

import java.util.UUID;

Expand All @@ -21,8 +21,24 @@
services = CodeIdsTestInstrument.class)
public class CodeIdsTestInstrument extends TruffleInstrument {
public static final String INSTRUMENT_ID = "ids-test";
Akirathan marked this conversation as resolved.
Show resolved Hide resolved
private static final Class<?> exprNodeClass;
private static final Method exprNodeGetId;
private static final Class<?> tailCallExceptionClass;
private Env env;

/**
* Use reflection to call methods from `runtime` project. We cannot use `runtime` as a dependency.
*/
static {
try {
exprNodeClass = Class.forName("org.enso.interpreter.node.ExpressionNode");
exprNodeGetId = exprNodeClass.getMethod("getId");
tailCallExceptionClass = Class.forName("org.enso.interpreter.runtime.control.TailCallException");
Akirathan marked this conversation as resolved.
Show resolved Hide resolved
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new AssertionError(e);
}
}

/**
* Initializes the instrument. Substitute for a constructor, called by the Truffle framework.
*
Expand Down Expand Up @@ -95,7 +111,6 @@ public void onEnter(VirtualFrame frame) {}
/**
* Checks if the node to be executed is the node this listener was created to observe.
*
* @param context current execution context
* @param frame current execution frame
* @param result the result of executing the node
*/
Expand All @@ -105,11 +120,11 @@ public void onReturnValue(VirtualFrame frame, Object result) {
return;
}
Node node = context.getInstrumentedNode();
if (!(node instanceof ExpressionNode)) {
if (!(isInstanceOf(node, exprNodeClass))) {
return;
}
nodes.put(this, result);
UUID id = ((ExpressionNode) node).getId();
UUID id = exprNodeGetId(node);
if (id == null || !id.equals(expectedId)) {
return;
}
Expand All @@ -118,6 +133,19 @@ public void onReturnValue(VirtualFrame frame, Object result) {
}
}

private boolean isInstanceOf(Object obj, Class<?> clazz) {
return clazz.isInstance(obj);
}

private UUID exprNodeGetId(Object exprNode) {
assert isInstanceOf(exprNode, exprNodeClass);
try {
return (UUID) exprNodeGetId.invoke(exprNode);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new AssertionError(e);
}
}

/**
* Checks if the specified was called, if its execution triggered TCO.
*
Expand All @@ -127,13 +155,13 @@ public void onReturnValue(VirtualFrame frame, Object result) {
*/
@Override
public void onReturnExceptional(VirtualFrame frame, Throwable exception) {
if (!(exception instanceof TailCallException)) {
if (!isInstanceOf(exception, tailCallExceptionClass)) {
return;
}
if (!(context.getInstrumentedNode() instanceof ExpressionNode)) {
if (!isInstanceOf(context.getInstrumentedNode(), exprNodeClass)) {
return;
}
UUID id = ((ExpressionNode) context.getInstrumentedNode()).getId();
UUID id = exprNodeGetId(context.getInstrumentedNode());
if (expectedResult == null) {
successful = true;
}
Expand All @@ -143,8 +171,9 @@ public void onReturnExceptional(VirtualFrame frame, Throwable exception) {
public String toString() {
var sb = new StringBuilder();
sb.append(context.getInstrumentedNode().getClass().getSimpleName());
if (context.getInstrumentedNode() instanceof ExpressionNode expr) {
sb.append("@").append(expr.getId());
if (isInstanceOf(context.getInstrumentedNode(), exprNodeClass)) {
UUID id = exprNodeGetId(context.getInstrumentedNode());
sb.append("@").append(id);
}
sb.append(" ");
sb.append(context.getInstrumentedSourceSection());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.enso.interpreter.test;
package org.enso.interpreter.test.instruments;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.EventBinding;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
open module org.enso.runtime.with.instruments {
requires org.enso.runtime;
requires org.graalvm.truffle;
// TODO: Provides instrument

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class RuntimeProjectContextTest
.toFile
.getAbsolutePath
)
.option("engine.WarnInterpreterOnly", "false")
.option(RuntimeOptions.EDITION_OVERRIDE, "0.0.0-dev")
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName)
.logHandler(System.err)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.enso.interpreter.test

import com.oracle.truffle.api.instrumentation.EventBinding
import org.enso.interpreter.test.CodeIdsTestInstrument.IdEventListener
import org.enso.interpreter.test.CodeLocationsTestInstrument.LocationsEventListener
import org.enso.interpreter.test.instruments.CodeIdsTestInstrument.IdEventListener
import org.enso.interpreter.test.instruments.{
CodeIdsTestInstrument,
CodeLocationsTestInstrument
}
import org.enso.interpreter.test.instruments.CodeLocationsTestInstrument.LocationsEventListener
import org.enso.polyglot.debugger.{
DebugServerInfo,
DebuggerSessionManagerEndpoint,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%logger{36}] -%kvp- %msg%n</pattern>
</encoder>
<!-- deny all events with a level below WARN, that is INFO, DEBUG and TRACE -->
<!-- Accepts only WARN and ERROR -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>

<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
182 changes: 182 additions & 0 deletions project/JPMSPlugin.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import sbt.*
import sbt.Keys.*
import sbt.internal.inc.{CompileOutput, PlainVirtualFile}
import sbt.util.CacheStore
import sbtassembly.Assembly.{Dependency, JarEntry, Project}
import sbtassembly.{CustomMergeStrategy, MergeStrategy}
import xsbti.compile.IncToolOptionsUtil

import java.io.File

/** An automatic plugin that handles everything related to JPMS modules. One needs to explicitly
* enable this plugin in a project with `.enablePlugins(JPMSPlugin)`. The keys and tasks provided by this plugin
* corresponds to the module-related options of `javac` and `java` commands.
*
* This plugin injects all the module-specific options to `javaOptions`, based on
* the settings of this plugin.
*
* If this plugin is enabled, and no settings/tasks from this plugin are used, then the plugin will
* not inject anything into `javaOptions` or `javacOptions`.
*/
object JPMSPlugin extends AutoPlugin {
object autoImport {
val javaModuleName =
settingKey[String]("The name of the Java (JPMS) module")
val addModules = settingKey[Seq[String]](
"Module names that will be added to --add-modules option"
)
val modulePath = taskKey[Seq[File]](
"Directories (Jar archives or expanded Jar archives) that will be put into " +
"--module-path option"
)
val patchModules = taskKey[Map[String, Seq[File]]](
"""
|A map of module names to directories (Jar archives or expanded Jar archives) that will be
|put into --patch-module option.
|""".stripMargin
)
val addExports = taskKey[Map[String, Seq[String]]](
"""
|A map of module names to packages that will be put into --add-exports option.
|The format of `--add-exports` option is `module/package=target-module(,target-module)*`
|The key in the map is `module/package` and the value is a sequence of target modules
|""".stripMargin
)
val addReads = taskKey[Map[String, Seq[String]]](
"""
|A map of module names to modules that will be put into --add-reads option.
|When a module A reads a module B, it means that it "depends" on it - it has the same
|effect as if module A would have `requires B` in its module-info.java file.
|""".stripMargin
)
val compileModuleInfo = taskKey[Unit]("Compile module-info.java")
val modulePathTestOptions_ = taskKey[Seq[String]](
"Assembles options for the JVM for running tests with all the required modules. " +
"Including truffle-compiler and org.enso.runtime modules and all their dependencies."
)
}

import autoImport.*

override lazy val projectSettings: Seq[Setting[_]] = Seq(
addModules := Seq.empty,
modulePath := Seq.empty,
patchModules := Map.empty,
addExports := Map.empty,
addReads := Map.empty,
compileModuleInfo := {},
// javacOptions only inject --module-path and --add-modules, not the rest of the
// options.
Compile / javacOptions ++= {
constructOptions(
streams.value.log,
modulePath = (Compile / modulePath).value,
addModules = (Compile / addModules).value
)
},
Compile / javaOptions ++= {
constructOptions(
streams.value.log,
(Compile / modulePath).value,
(Compile / addModules).value,
(Compile / patchModules).value,
(Compile / addExports).value,
(Compile / addReads).value
)
},
Test / javacOptions ++= {
constructOptions(
streams.value.log,
modulePath = (Test / modulePath).value,
addModules = (Test / addModules).value
)
},
Test / javaOptions ++= {
constructOptions(
streams.value.log,
(Test / modulePath).value,
(Test / addModules).value,
(Test / patchModules).value,
(Test / addExports).value,
(Test / addReads).value
)
}
)

def constructOptions(
log: Logger,
modulePath: Seq[File],
addModules: Seq[String] = Seq.empty,
patchModules: Map[String, Seq[File]] = Map.empty,
addExports: Map[String, Seq[String]] = Map.empty,
addReads: Map[String, Seq[String]] = Map.empty
): Seq[String] = {
val patchOpts: Seq[String] = patchModules.flatMap {
case (moduleName, dirsToPatch) =>
ensureDirectoriesExist(dirsToPatch, log)
val patchStr = dirsToPatch
.map(_.getAbsolutePath)
.mkString(File.pathSeparator)
Seq(
"--patch-module",
s"$moduleName=$patchStr"
)
}.toSeq

ensureDirectoriesExist(modulePath, log)

val addExportsOpts: Seq[String] = addExports.flatMap {
case (modPkgName, targetModules) =>
if (!modPkgName.contains("/")) {
log.error(s"JPMSPlugin: Invalid module/package name: $modPkgName")
}
Seq(
"--add-exports",
modPkgName + "=" + targetModules.mkString(",")
)
}.toSeq

val modulePathOpts = if (modulePath.isEmpty) {
Seq.empty
} else {
Seq(
"--module-path",
modulePath.map(_.getAbsolutePath).mkString(File.pathSeparator)
)
}

val addModsOpts = if (addModules.isEmpty) {
Seq.empty
} else {
Seq(
"--add-modules",
addModules.mkString(",")
)
}

val addReadsOpts = addReads.flatMap { case (modName, targetModules) =>
Seq(
"--add-reads",
modName + "=" + targetModules.mkString(",")
)
}.toSeq

modulePathOpts ++ addModsOpts ++ patchOpts ++ addExportsOpts ++ addReadsOpts
}

/** Java does not mandate that the directories specified in the module path or
* in --patch-module exist, but it is usefull to report at least warnings.
* @param dirs
* @param log
*/
private def ensureDirectoriesExist(
dirs: Seq[File],
log: Logger
): Unit = {
dirs.foreach { dir =>
if (!dir.exists()) {
log.warn(s"JPMSPlugin: Directory $dir does not exist.")
}
}
}
}
Loading
Loading