Reducing memory leaks in runtime-integration-tests (#10793)

Inspired by the revert done in https://github.com/enso-org/enso/pull/10778, started looking into apparent memory leaks in `runtime-integration-tests`, written in Java.

Initial state:
![Screenshot from 2024-08-09 14-36-29](https://github.com/user-attachments/assets/39abd48f-503b-49d8-af97-da051352c70d)

After:
![Screenshot from 2024-08-12 16-33-40](https://github.com/user-attachments/assets/2bf4cc2d-7e0e-4d22-8810-c2e7e5c3b065)

# Important Notes
Some remaining issues:
- [ ] [TCK tests](https://github.com/enso-org/enso/tree/develop/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/tck) appear to have some memory leaks but we are essentially enabling them via simple inheritance
- [ ] [RuntimeManagementTest](https://github.com/enso-org/enso/blob/develop/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/RuntimeManagementTest.scala) appears to be broken as it doesn't seem to shutdown properly created threads:
![Screenshot from 2024-08-12 16-29-09](https://github.com/user-attachments/assets/d90aca62-0562-4287-88b7-6d4719e5cf50)

Leaving this for now, as it will probably need to be taken care by initial authors of those tests, if possible. Plus this PR leaves tests in a much better state than before.
This commit is contained in:
Hubert Plociniczak 2024-08-13 10:01:31 +02:00 committed by GitHub
parent 31e589e362
commit ff7e31c237
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 362 additions and 204 deletions

View File

@ -105,6 +105,7 @@ trait PackageRepository {
context: CompilerContext
): Option[IRModule]
def shutdown(): Unit
}
object PackageRepository {

View File

@ -17,8 +17,8 @@ import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.io.IOAccess;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -45,7 +45,7 @@ public class ExecStrictCompilerTest {
assertNotNull("Enso language is supported", ctx.getEngine().getLanguages().get("enso"));
}
@Before
@After
public void cleanMessages() {
MESSAGES.reset();
}
@ -53,6 +53,7 @@ public class ExecStrictCompilerTest {
@AfterClass
public static void closeEnsoContext() {
ctx.close();
ctx = null;
}
@Test

View File

@ -47,13 +47,14 @@ public class ExportedSymbolsTest {
type A_Type
""");
ProjectUtils.createProject("Proj", Set.of(mainSrcMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.containsKey("A_Type"), is(true));
assertThat(
mainExportedSymbols.get("A_Type").get(0), instanceOf(BindingsMap.ResolvedType.class));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.containsKey("A_Type"), is(true));
assertThat(
mainExportedSymbols.get("A_Type").get(0), instanceOf(BindingsMap.ResolvedType.class));
}
}
@Test
@ -70,11 +71,12 @@ public class ExportedSymbolsTest {
type B_Type
""");
ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(2));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type"));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(2));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type"));
}
}
@Test
@ -91,11 +93,12 @@ public class ExportedSymbolsTest {
type B_Type
""");
ProjectUtils.createProject("Proj", Set.of(mainMod, bMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(2));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type"));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(2));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type"));
}
}
@Test
@ -111,11 +114,12 @@ public class ExportedSymbolsTest {
export project.A_Module.A_Type as Foo
""");
ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Foo"));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Foo"));
}
}
@Test
@ -133,13 +137,16 @@ public class ExportedSymbolsTest {
import project.Synthetic_Module
""");
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var syntheticModExpSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Synthetic_Module");
assertThat(
"Just a A_Module submodule should be exported", syntheticModExpSymbols.size(), is(1));
assertThat(
"Just a A_Module submodule should be exported", syntheticModExpSymbols, hasKey("A_Module"));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var syntheticModExpSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Synthetic_Module");
assertThat(
"Just a A_Module submodule should be exported", syntheticModExpSymbols.size(), is(1));
assertThat(
"Just a A_Module submodule should be exported",
syntheticModExpSymbols,
hasKey("A_Module"));
}
}
@Test
@ -156,14 +163,16 @@ public class ExportedSymbolsTest {
export project.A_Module.A_Module
""");
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module"));
assertThat(mainExportedSymbols.get("A_Module").size(), is(1));
assertThat(
mainExportedSymbols.get("A_Module").get(0), is(instanceOf(BindingsMap.ResolvedType.class)));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module"));
assertThat(mainExportedSymbols.get("A_Module").size(), is(1));
assertThat(
mainExportedSymbols.get("A_Module").get(0),
is(instanceOf(BindingsMap.ResolvedType.class)));
}
}
@Test
@ -179,15 +188,16 @@ public class ExportedSymbolsTest {
export project.A_Module
""");
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module"));
assertThat(mainExportedSymbols.get("A_Module").size(), is(1));
assertThat(
mainExportedSymbols.get("A_Module").get(0),
is(instanceOf(BindingsMap.ResolvedModule.class)));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module"));
assertThat(mainExportedSymbols.get("A_Module").size(), is(1));
assertThat(
mainExportedSymbols.get("A_Module").get(0),
is(instanceOf(BindingsMap.ResolvedModule.class)));
}
}
@Test
@ -205,15 +215,16 @@ public class ExportedSymbolsTest {
export project.Synthetic_Module
""");
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir);
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Synthetic_Module"));
assertThat(mainExportedSymbols.get("Synthetic_Module").size(), is(1));
assertThat(
mainExportedSymbols.get("Synthetic_Module").get(0),
is(instanceOf(BindingsMap.ResolvedModule.class)));
try (var ctx = createCtx(projDir)) {
compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Synthetic_Module"));
assertThat(mainExportedSymbols.get("Synthetic_Module").size(), is(1));
assertThat(
mainExportedSymbols.get("Synthetic_Module").get(0),
is(instanceOf(BindingsMap.ResolvedModule.class)));
}
}
private static Context createCtx(Path projDir) {

View File

@ -103,6 +103,7 @@ main = 42
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
private final Symbol symbol;

View File

@ -74,7 +74,7 @@ public class SerdeCompilerTest {
var persisted = f.get(10, TimeUnit.SECONDS);
assertEquals("Fib_Test library has been fully persisted", true, persisted);
}
old = module.getIr();
old = module.getIr().duplicate(true, true, true, false);
ctx.leave();
}
@ -103,7 +103,7 @@ public class SerdeCompilerTest {
var mainValue = ctx.asValue(main);
assertEquals(42, mainValue.execute().asInt());
now = module.getIr();
now = module.getIr().duplicate(true, true, true, false);
ctx.leave();
}

View File

@ -28,11 +28,12 @@ public class SerializationManagerTest {
private static final long COMPILE_TIMEOUT_SECONDS = 20;
private final PackageManager<TruffleFile> packageManager;
private final InterpreterContext interpreterContext;
private final EnsoContext ensoContext;
private PackageManager<TruffleFile> packageManager;
private InterpreterContext interpreterContext;
private EnsoContext ensoContext;
public SerializationManagerTest() {
@Before
public void setup() {
packageManager = new PackageManager<>(new TruffleFileSystem());
interpreterContext = new InterpreterContext(x -> x);
ensoContext =
@ -41,22 +42,20 @@ public class SerializationManagerTest {
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject();
}
@Before
public void setup() {
interpreterContext.ctx().initialize(LanguageInfo.ID);
interpreterContext.ctx().enter();
}
@After
public void teardown() {
interpreterContext.ctx().close();
interpreterContext.close();
ensoContext.shutdown();
ensoContext = null;
}
private Path getLibraryPath(LibraryName libraryName) {
return Paths.get(
interpreterContext.languageHome(),
interpreterContext.languageHome().toFile().getAbsolutePath(),
"..",
"lib",
libraryName.namespace(),

View File

@ -42,29 +42,29 @@ public class SerializerTest {
var pkgPath = new File(getClass().getClassLoader().getResource(testName).getPath());
var pkg = PackageManager.Default().fromDirectory(pkgPath).get();
var ctx = ensoContextForPackage(testName, pkgPath);
var ensoContext =
(EnsoContext)
ctx.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject();
var mainModuleOpt = ensoContext.getModuleForFile(pkg.mainFile());
assertEquals(mainModuleOpt.isPresent(), true);
try (var ctx = ensoContextForPackage(testName, pkgPath)) {
var ensoContext =
(EnsoContext)
ctx.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject();
var mainModuleOpt = ensoContext.getModuleForFile(pkg.mainFile());
assertEquals(mainModuleOpt.isPresent(), true);
var compiler = ensoContext.getCompiler();
var module = mainModuleOpt.get().asCompilerModule();
var compiler = ensoContext.getCompiler();
var module = mainModuleOpt.get().asCompilerModule();
ctx.enter();
var result = compiler.run(module);
assertEquals(result.compiledModules().exists(m -> m == module), true);
var useThreadPool = compiler.context().isCreateThreadAllowed();
var future = compiler.context().serializeModule(compiler, module, true, useThreadPool);
var serialized = future.get(5, TimeUnit.SECONDS);
assertEquals(serialized, true);
var deserialized = compiler.context().deserializeModule(compiler, module);
assertTrue("Deserialized", deserialized);
compiler.context().shutdown(true);
ctx.leave();
ctx.close();
ctx.enter();
var result = compiler.run(module);
assertEquals(result.compiledModules().exists(m -> m == module), true);
var useThreadPool = compiler.context().isCreateThreadAllowed();
var future = compiler.context().serializeModule(compiler, module, true, useThreadPool);
var serialized = future.get(5, TimeUnit.SECONDS);
assertEquals(serialized, true);
var deserialized = compiler.context().deserializeModule(compiler, module);
assertTrue("Deserialized", deserialized);
compiler.context().shutdown(true);
ctx.leave();
}
}
}

View File

@ -35,6 +35,7 @@ public class VectorArraySignatureTest {
@AfterClass
public static void closeEnsoParser() throws Exception {
ensoCompiler.close();
ensoCompiler = null;
}
@Test

View File

@ -19,8 +19,7 @@ public class IRDumpTest {
System.setProperty(IRDumper.SYSTEM_PROP, "true");
try (var ctx = ContextUtils.defaultContextBuilder().out(out).build()) {
// Dumping is done in the compiler, so it is enough just to compile the module
var moduleIr =
ContextUtils.compileModule(ctx, """
ContextUtils.compileModule(ctx, """
main = 42
""", "MyMainModule");
assertThat(
@ -39,6 +38,7 @@ public class IRDumpTest {
} catch (IOException e) {
// Ignore. The ir-dumps directory should be deleted eventually.
}
out.reset();
}
}
}

View File

@ -14,6 +14,7 @@ import org.enso.interpreter.runtime.EnsoContext;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@ -30,6 +31,12 @@ public class ModuleCacheTest {
.build();
}
@AfterClass
public static void disposeContext() {
ctx.close();
ctx = null;
}
@Test
public void testCompareList() throws Exception {
var ensoCtx =

View File

@ -27,6 +27,7 @@ public class PolyglotCallTypeTest {
@AfterClass
public static void closeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -66,6 +66,7 @@ public class TypeOfNodeTest {
public static void disposeCtx() throws Exception {
if (ctx != null) {
ctx.close();
ctx = null;
}
}

View File

@ -42,6 +42,7 @@ public class ModuleSourcesTest {
public void cleanup() {
f.delete();
this.ctx.close();
this.ctx = null;
}
@Test

View File

@ -50,6 +50,7 @@ public class ModuleTest {
public void cleanup() {
f.delete();
this.ctx.close();
this.ctx = null;
}
@Test

View File

@ -31,6 +31,7 @@ public class AtomConstructorTest {
@AfterClass
public static void closeContext() {
ctx.close();
ctx = null;
}
@Test

View File

@ -27,6 +27,7 @@ public class AtomInteropTest {
@After
public void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -12,8 +12,8 @@ import org.enso.common.MethodNames;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -28,7 +28,7 @@ public class AutoscopedConstructorTest {
ctx = ContextUtils.createDefaultContext(out);
}
@Before
@After
public void resetOut() {
out.reset();
}
@ -36,6 +36,7 @@ public class AutoscopedConstructorTest {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -28,6 +28,7 @@ public class BigNumberTest {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -63,7 +63,9 @@ public class BinaryDispatchTest {
@AfterClass
public static void closeCtx() {
module = null;
ctx.close();
ctx = null;
}
@Test

View File

@ -89,6 +89,7 @@ public class BinaryOpFloatTest {
@AfterClass
public static void closeContext() {
ctx.close();
ctx = null;
}
private final String operation;

View File

@ -108,6 +108,7 @@ public class BinaryOpIntegerTest {
@AfterClass
public static void closeContext() {
ctx.close();
ctx = null;
}
private final String operation;

View File

@ -0,0 +1,27 @@
package org.enso.interpreter.test;
import java.io.OutputStream;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public class ContextTest {
protected static Context ctx;
@BeforeClass
public static void prepareCtx() {
ctx =
ContextUtils.defaultContextBuilder()
.out(OutputStream.nullOutputStream())
.err(OutputStream.nullOutputStream())
.build();
}
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
}

View File

@ -10,8 +10,8 @@ import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -28,9 +28,10 @@ public class ConversionMethodTests {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Before
@After
public void resetOutput() {
out.reset();
}

View File

@ -79,7 +79,9 @@ public class DebuggingEnsoTest {
@After
public void disposeContext() {
context.close();
context = null;
engine.close();
engine = null;
}
private static void expectStackFrame(

View File

@ -55,6 +55,7 @@ public class DiagnosticFormatterTest {
@After
public void closeCtx() throws IOException {
ctx.close();
ctx = null;
output.close();
}

View File

@ -23,6 +23,7 @@ public class EqualsConversionsTest {
@AfterClass
public static void disposeContext() {
context.close();
context = null;
}
@Test

View File

@ -61,6 +61,8 @@ public class EqualsTest {
@AfterClass
public static void disposeContext() {
context.close();
context = null;
unwrappedValues = null;
}
@DataPoints public static Object[] unwrappedValues;
@ -90,6 +92,8 @@ public class EqualsTest {
.toArray(new Object[] {});
} catch (Exception e) {
throw new AssertionError(e);
} finally {
valGenerator.dispose();
}
}

View File

@ -29,6 +29,7 @@ public class FindExceptionMessageTest {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -27,6 +27,7 @@ public class ForeignMethodInvokeTest {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -53,6 +53,8 @@ public class HashCodeTest {
@AfterClass
public static void disposeContext() {
context.close();
context = null;
unwrappedValues = null;
}
/**

View File

@ -60,7 +60,9 @@ public class InsightForEnsoTest {
@After
public void disposeContext() throws Exception {
this.insightHandle.close();
this.out.reset();
this.ctx.close();
ctx = null;
}
@Test

View File

@ -40,6 +40,7 @@ public class IntegerTest {
@AfterClass
public static void teardown() {
ctx.close();
ctx = null;
}
private static final EnsoBigInteger bigInt =

View File

@ -12,8 +12,8 @@ import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -30,9 +30,10 @@ public class JavaInteropTest {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Before
@After
public void resetOutput() {
out.reset();
}

View File

@ -18,12 +18,13 @@ public class JsInteropTest {
@Before
public void initContext() {
ctx = ContextUtils.createDefaultContext(out);
out.reset();
}
@After
public void disposeCtx() {
ctx.close();
ctx = null;
out.reset();
}
@Test

View File

@ -64,7 +64,15 @@ public class ListTest {
@After
public void disposeCtx() {
generator = null;
plusOne = null;
evenOnes = null;
taken = null;
init = null;
asVector = null;
asText = null;
this.ctx.close();
ctx = null;
}
@Test

View File

@ -53,7 +53,10 @@ public class MetaIsATest {
generator.dispose();
generator = null;
}
isACheck = null;
warningCheck = null;
ctx.close();
ctx = null;
}
/**

View File

@ -38,6 +38,7 @@ public class NonStrictModeTests {
@AfterClass
public static void disposeCtx() {
nonStrictCtx.close();
nonStrictCtx = null;
}
@Before

View File

@ -93,7 +93,9 @@ public class PolyglotErrorTest {
@After
public void disposeCtx() {
this.panic = null;
this.ctx.close();
ctx = null;
}
@Test

View File

@ -55,6 +55,8 @@ public class RootNamesTest {
public void disposeContext() throws Exception {
this.insightHandle.close();
this.ctx.close();
this.ctx = null;
this.out.reset();
}
@Test

View File

@ -6,36 +6,16 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import org.enso.common.MethodNames;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class SignatureTest {
private static Context ctx;
@BeforeClass
public static void prepareCtx() {
ctx =
ContextUtils.defaultContextBuilder()
.out(OutputStream.nullOutputStream())
.err(OutputStream.nullOutputStream())
.build();
}
@AfterClass
public static void disposeCtx() {
ctx.close();
}
public class SignatureTest extends ContextTest {
@Test
public void wrongFunctionSignature() throws Exception {

View File

@ -3,32 +3,12 @@ package org.enso.interpreter.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.OutputStream;
import java.net.URI;
import org.enso.common.MethodNames;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class SymbolResolutionTest {
private static Context ctx;
@BeforeClass
public static void prepareCtx() {
ctx =
ContextUtils.defaultContextBuilder()
.out(OutputStream.nullOutputStream())
.err(OutputStream.nullOutputStream())
.build();
}
@AfterClass
public static void disposeCtx() {
ctx.close();
}
public class SymbolResolutionTest extends ContextTest {
@Test
public void resolvingLocalSymbol() throws Exception {

View File

@ -10,8 +10,8 @@ import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -34,7 +34,7 @@ public class TypeInferenceConsistencyTest {
.build();
}
@Before
@After
public void cleanMessages() {
output.reset();
}
@ -46,6 +46,7 @@ public class TypeInferenceConsistencyTest {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -28,6 +28,7 @@ public class TypeMembersTest {
@After
public void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -47,12 +47,14 @@ public class VectorSortTest {
values.addAll(valuesGenerator.booleans());
values.addAll(valuesGenerator.durations());
values.addAll(valuesGenerator.maps());
valuesGenerator.dispose();
}
@AfterClass
public static void disposeCtx() {
values.clear();
context.close();
context = null;
}
@DataPoints public static List<Value> values;

View File

@ -27,6 +27,7 @@ public class VectorTest {
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
@Test

View File

@ -48,8 +48,11 @@ public class WarningsTest {
@AfterClass
public static void disposeContext() {
ensoContext = null;
generator.dispose();
ctx.close();
ctx = null;
ensoContext.shutdown();
ensoContext = null;
}
@Test

View File

@ -42,6 +42,7 @@ public class AssertionsTest {
@AfterClass
public static void disposeCtx() {
ctx.close(true);
ctx = null;
}
@After

View File

@ -27,6 +27,7 @@ public class DisabledAssertionsTest {
@AfterClass
public static void disposeCtx() {
ctx.close(true);
ctx = null;
}
@Test

View File

@ -36,6 +36,7 @@ public class SuccessfulAssertionExpressionTest {
@AfterClass
public static void disposeCtx() {
ctx.close(true);
ctx = null;
}
@After

View File

@ -44,7 +44,9 @@ public class AvoidIdInstrumentationTagTest {
@After
public void disposeContext() {
nodes = null;
context.close();
context = null;
}
@Test

View File

@ -48,6 +48,7 @@ public class FunctionPointerTest {
@After
public void disposeContext() {
context.close();
context = null;
}
@Test

View File

@ -68,7 +68,9 @@ public class IncrementalUpdatesTest {
@After
public void teardownContext() {
nodeCountingInstrument = null;
context.close();
context = null;
}
@Test

View File

@ -62,7 +62,9 @@ public class WarningInstrumentationTest {
@After
public void disposeContext() {
instrument = null;
context.close();
context = null;
}
@Test

View File

@ -16,7 +16,8 @@ import java.util.UUID
class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
private val ctx = new InterpreterContext()
private val langCtx = ctx.ctx
private val langCtx = ctx
.ctx()
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]()

View File

@ -71,13 +71,15 @@ trait ModifiedTest
.build()
context.initialize(LanguageInfo.ID)
val executionContext = new PolyglotContext(context)
InterpreterException.rethrowPolyglot {
val result = InterpreterException.rethrowPolyglot {
val topScope = executionContext.getTopScope
val mainModuleScope = topScope.getModule(mainModule.toString)
val assocCons = mainModuleScope.getAssociatedType
val mainFun = mainModuleScope.getMethod(assocCons, "main").get
mainFun.execute()
}
context.close()
result
}
private def initialCopy(from: File, to: File): Unit = {

View File

@ -16,7 +16,8 @@ import java.util.UUID
class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
private val ctx = new InterpreterContext()
private val langCtx = ctx.ctx
private val langCtx = ctx
.ctx()
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]()

View File

@ -130,7 +130,8 @@ class TypeSignaturesTest
with TypeMatchers {
private val ctx = new InterpreterContext()
private val langCtx = ctx.ctx
private val langCtx = ctx
.ctx()
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]()

View File

@ -15,7 +15,6 @@ import org.enso.polyglot.debugger.{
}
import org.enso.common.LanguageInfo
import org.enso.common.RuntimeOptions
import org.enso.polyglot.{Function, PolyglotContext}
import org.graalvm.polyglot.{Context, Value}
import org.scalatest.Assertions
@ -28,7 +27,7 @@ import java.io.{
PipedOutputStream,
PrintStream
}
import java.nio.file.Paths
import java.nio.file.{Path, Paths}
import java.util.UUID
import java.util.logging.Level
@ -104,43 +103,101 @@ class ReplaceableSessionManager extends SessionManager {
class InterpreterContext(
contextModifiers: Context#Builder => Context#Builder = bldr => bldr
) {
val output = new ByteArrayOutputStream()
val err = new ByteArrayOutputStream()
val inOut = new PipedOutputStream()
val inOutPrinter = new PrintStream(inOut, true)
val in = new PipedInputStream(inOut)
val sessionManager = new ReplaceableSessionManager
var _ctx: Context = _
var _err: ByteArrayOutputStream = _
var _output: ByteArrayOutputStream = new ByteArrayOutputStream()
var _in: PipedInputStream = _
var _inOut: PipedOutputStream = _
var _inOutPrinter: PrintStream = _
var _sessionManager: ReplaceableSessionManager = new ReplaceableSessionManager
var _languageHome: Path = _
val edition = "0.0.0-dev"
def ctx(): Context = {
if (_ctx == null) {
_output = new ByteArrayOutputStream()
_err = new ByteArrayOutputStream()
_inOut = new PipedOutputStream()
_inOutPrinter = new PrintStream(_inOut, true)
_in = new PipedInputStream(_inOut)
val languageHome = Paths
.get("../../test/micro-distribution/component")
.toFile
.getAbsolutePath
val edition = "0.0.0-dev"
_languageHome = Paths
.get("../../test/micro-distribution/component")
val ctx = contextModifiers(
Context
.newBuilder(LanguageInfo.ID)
.allowExperimentalOptions(true)
.allowAllAccess(true)
.allowCreateThread(false)
.out(output)
.err(err)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.option(RuntimeOptions.DISABLE_IR_CACHES, "true")
.option(RuntimeOptions.STRICT_ERRORS, "false")
.environment("NO_COLOR", "true")
.logHandler(System.err)
.in(in)
.option(RuntimeOptions.LANGUAGE_HOME_OVERRIDE, languageHome)
.option(RuntimeOptions.EDITION_OVERRIDE, edition)
.option("engine.WarnInterpreterOnly", "false")
.serverTransport { (uri, peer) =>
if (uri.toString == DebugServerInfo.URI) {
new DebuggerSessionManagerEndpoint(sessionManager, peer)
} else null
}
).build()
lazy val executionContext = new PolyglotContext(ctx)
_ctx = contextModifiers(
Context
.newBuilder(LanguageInfo.ID)
.allowExperimentalOptions(true)
.allowAllAccess(true)
.allowCreateThread(false)
.out(_output)
.err(_err)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.option(RuntimeOptions.DISABLE_IR_CACHES, "true")
.option(RuntimeOptions.STRICT_ERRORS, "false")
.environment("NO_COLOR", "true")
.logHandler(System.err)
.in(_in)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
_languageHome.toFile.getAbsolutePath
)
.option(RuntimeOptions.EDITION_OVERRIDE, edition)
.option("engine.WarnInterpreterOnly", "false")
.serverTransport { (uri, peer) =>
if (uri.toString == DebugServerInfo.URI) {
new DebuggerSessionManagerEndpoint(_sessionManager, peer)
} else null
}
).build()
}
_ctx
}
def err(): ByteArrayOutputStream = {
assert(_ctx != null)
_err
}
def output(): ByteArrayOutputStream = {
_output
}
def in(): PipedInputStream = {
assert(_ctx != null)
_in
}
def inOut(): PipedOutputStream = {
assert(_ctx != null)
_inOut;
}
def inOutPrinter(): PrintStream = {
assert(_ctx != null)
_inOutPrinter
}
def sessionManager(): ReplaceableSessionManager = {
_sessionManager
}
def languageHome(): Path = {
assert(_ctx != null)
_languageHome
}
def close(): Unit = {
if (_ctx != null) {
_ctx.close()
_ctx = null
}
_output.reset()
if (_err != null) _err.close()
if (_in != null) _in.close()
if (_inOut != null) _inOut.close()
}
lazy val executionContext = new PolyglotContext(ctx())
}
trait InterpreterRunner {
@ -154,7 +211,10 @@ trait InterpreterRunner {
def withLocationsInstrumenter(
test: LocationsInstrumenter => Unit
)(implicit interpreterContext: InterpreterContext): Unit = {
val instrument = interpreterContext.ctx.getEngine.getInstruments
val instrument = interpreterContext
.ctx()
.getEngine
.getInstruments
.get(CodeLocationsTestInstrument.INSTRUMENT_ID)
.lookup(classOf[CodeLocationsTestInstrument])
val instrumenter = LocationsInstrumenter(instrument)
@ -166,7 +226,10 @@ trait InterpreterRunner {
def withIdsInstrumenter(
test: IdsInstrumenter => Unit
)(implicit interpreterContext: InterpreterContext): Unit = {
val instrument = interpreterContext.ctx.getEngine.getInstruments
val instrument = interpreterContext
.ctx()
.getEngine
.getInstruments
.get(CodeIdsTestInstrument.INSTRUMENT_ID)
.lookup(classOf[CodeIdsTestInstrument])
val instrumenter = IdsInstrumenter(instrument)
@ -185,7 +248,7 @@ trait InterpreterRunner {
def getMain(
code: String
)(implicit interpreterContext: InterpreterContext): MainMethod = {
interpreterContext.output.reset()
interpreterContext.output().reset()
val module = InterpreterException.rethrowPolyglot(
interpreterContext.executionContext.evalModule(code, "Test")
)
@ -206,52 +269,52 @@ trait InterpreterRunner {
def consumeErr(implicit
interpreterContext: InterpreterContext
): List[String] = {
val result = interpreterContext.err.toString
interpreterContext.err.reset()
val result = interpreterContext.err().toString
interpreterContext.err().reset()
result.linesIterator.toList
}
def consumeErrBytes(implicit
interpreterContext: InterpreterContext
): Array[Byte] = {
val result = interpreterContext.err.toByteArray
interpreterContext.err.reset()
val result = interpreterContext.err().toByteArray
interpreterContext.err().reset()
result
}
def consumeOut(implicit
interpreterContext: InterpreterContext
): List[String] = {
val result = interpreterContext.output.toString
interpreterContext.output.reset()
val result = interpreterContext.output().toString
interpreterContext.output().reset()
result.linesIterator.toList
}
def consumeOutBytes(implicit
interpreterContext: InterpreterContext
): Array[Byte] = {
val result = interpreterContext.output.toByteArray
interpreterContext.output.reset()
val result = interpreterContext.output().toByteArray
interpreterContext.output().reset()
result
}
def feedInput(
string: String
)(implicit interpreterContext: InterpreterContext): Unit = {
interpreterContext.inOutPrinter.println(string)
interpreterContext.inOutPrinter().println(string)
}
def feedBytes(
input: Array[Byte]
)(implicit interpreterContext: InterpreterContext): Unit = {
interpreterContext.inOut.write(input)
interpreterContext.inOut.flush()
interpreterContext.inOut().write(input)
interpreterContext.inOut().flush()
}
def setSessionManager(
manager: SessionManager
)(implicit interpreterContext: InterpreterContext): Unit =
interpreterContext.sessionManager.setSessionManager(manager)
interpreterContext.sessionManager().setSessionManager(manager)
// For Enso raw text blocks inside scala multiline strings
val rawTQ = "\"\"\""

View File

@ -36,7 +36,7 @@ class RuntimeServerTest
val out: ByteArrayOutputStream = new ByteArrayOutputStream()
val logOut: ByteArrayOutputStream = new ByteArrayOutputStream()
val context =
protected val context =
Context
.newBuilder(LanguageInfo.ID)
.allowExperimentalOptions(true)

View File

@ -15,7 +15,8 @@ class RuntimeManagementTest extends InterpreterTest {
): Unit = {
"Interrupt threads through Thread#interrupt()" in {
val langCtx = interpreterContext.ctx
val langCtx = interpreterContext
.ctx()
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]()

View File

@ -290,6 +290,10 @@ public final class EnsoContext {
threadManager.shutdown();
resourceManager.shutdown();
compiler.shutdown(shouldWaitForPendingSerializationJobs);
packageRepository.shutdown();
guestJava = null;
topScope = null;
hostClassLoader.close();
}
private boolean shouldAssertionsBeEnabled() {

View File

@ -17,7 +17,7 @@ import org.slf4j.LoggerFactory;
* the classes that are loaded via this class loader are first searched inside those archives. If
* not found, delegates to parent class loaders.
*/
final class HostClassLoader extends URLClassLoader {
final class HostClassLoader extends URLClassLoader implements AutoCloseable {
private final Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<>();
private static final Logger logger = LoggerFactory.getLogger(HostClassLoader.class);
@ -84,4 +84,9 @@ final class HostClassLoader extends URLClassLoader {
return super.findResources(name);
}
}
@Override
public void close() {
loadedClasses.clear();
}
}

View File

@ -121,6 +121,7 @@ final class SerializationPool {
t.join();
}
context.logSerializationManager(Level.FINE, "Serialization manager has been shut down.");
threads.clear();
}
}

View File

@ -610,6 +610,13 @@ private class DefaultPackageRepository(
}
else Failure(PackageManager.PackageNotFound())
}
override def shutdown(): Unit = {
loadedPackages.clear()
loadedModules.clear()
loadedComponents.clear()
loadedLibraryBindings.clear()
}
}
private object DefaultPackageRepository {