Skip to content
8 changes: 8 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "BreakTest",
"request": "launch",
"mainClass": "org.rascalmpl.compiler.BreakTest",
"projectName": "rascal",
"console": "integratedTerminal"
},
{
"type": "java",
"name": "RascalCheck",
Expand Down
117 changes: 117 additions & 0 deletions src/org/rascalmpl/compiler/BreakTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.rascalmpl.compiler;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.rascalmpl.debug.IRascalMonitor;
import org.rascalmpl.shell.RascalShell;
import org.rascalmpl.shell.ShellEvaluatorFactory;
import org.rascalmpl.test.infrastructure.RascalJUnitTestRunner;
import org.rascalmpl.uri.URIResolverRegistry;

import io.usethesource.vallang.ISourceLocation;

public class BreakTest {

private static final String BREAKING_MODULE = "lang::rascalcore::check::tests::ChangeScenarioTests";
private static final String BREAKING_TEST = "fixedErrorsDisappear2";

private static final int PARALLEL_RUNS = 8; // set to 1 to avoid any multi-threading interactions, but it might take 20 rounds or something
private static final int TRIES = 1000 / PARALLEL_RUNS;

public static void main(String[] args) throws IOException, InterruptedException {
RascalShell.setupJavaProcessForREPL();

var term = RascalShell.connectToTerminal();
var monitor = IRascalMonitor.buildConsoleMonitor(term);
var error = monitor instanceof PrintWriter ? (PrintWriter) monitor : new PrintWriter(System.err, false);
try {
AtomicBoolean failed = new AtomicBoolean(false);
AtomicInteger done = new AtomicInteger(0);
for (int t = 0; t < PARALLEL_RUNS; t++) {
var name = "Thread " + (t + 1);
var tr = new Thread(() -> {
try {
if (crashTest(monitor, error, name, failed)) {
failed.set(true);
System.err.println("We got a failure, exiting now!");
Thread.sleep(1000);
System.exit(1);
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.incrementAndGet();
}
});
tr.start();
}
while (done.get() < PARALLEL_RUNS && !failed.get()) {
Thread.sleep(100);
}
} finally {
error.close();
}
}

static boolean crashTest(IRascalMonitor monitor, PrintWriter errorPrinter, String name, AtomicBoolean failed) {
var output = new StringWriter();
var iFailed = new AtomicBoolean(false);
try (var err = new PrintWriter(output, true); var out= new PrintWriter(output, false)) {
var projectRoot = RascalJUnitTestRunner.inferProjectRootFromClass(BreakTest.class);
var evaluator = ShellEvaluatorFactory.getDefaultEvaluatorForLocation(projectRoot, Reader.nullReader(), err, out, monitor, "$test-"+name+"$");
evaluator.getConfiguration().setErrors(true);
// make sure we're writing to the outputs
evaluator.overwritePrintWriter(out, err);

evaluator.doImport(monitor, BREAKING_MODULE);
try {
monitor.job(name, TRIES, (jobname, step) -> {
try {
for (int i = 0; i < TRIES; i++) {
if (failed.get()) {
return false;
}
monitor.jobStep(jobname, "Running: try " + (i + 1));
evaluator.call(BREAKING_TEST);
}

} catch (Throwable e ) {
failed.set(true);
iFailed.set(true);
err.println("❌ test fail ");
err.println(e);
}
return null;
});
} finally {
// clean up memory
var memoryModule = evaluator.getHeap().getModule("lang::rascalcore::check::TestShared");
var testRoot = memoryModule.getFrameVariable("testRoot");
try {
URIResolverRegistry.getInstance().remove((ISourceLocation)testRoot.getValue(), true);
}
catch (Throwable e) {
err.println("Failure to cleanup the cache");
}
}

}

if (iFailed.get()) {
errorPrinter.println("❌❌❌ Test run failed: " + name);
errorPrinter.println("Job output:");
errorPrinter.println(output.toString());
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ tuple[bool, TModel, ModuleStatus] addGrammar(MODID moduleId, set[MODID] imports,
<found, tm1, ms> = getTModelForModule(m, ms);
if(!found) {
msg = error("Cannot add grammar or tmodel since `<moduleId2moduleName(m)>` is not found", ms.moduleLocs[moduleId] ? |unknown:///|);
println(msg); // TODO: Just to record this event; this should probably go to a log file
//println(msg); // TODO: Just to record this event; this should probably go to a log file
ms.messages[moduleId] ? {} += { msg };
tm1 = tmodel(modelName=qualifiedModuleName, messages=[msg]);
return <false, tm1, ms>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ bool uptodateTPls(list[loc] candidates, list[str] mnames, PathConfig pcfg){
for(int i <- index(candidates)){
mloc = candidates[i];
<found, tpl> = getTPLReadLoc(mnames[i], pcfg);
if(!found || lastModified(mloc) > lastModified(tpl)){
if(!found || lastModified(mloc) >= lastModified(tpl)){
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ bool tplOutdated(MODID moduleId, PathConfig pcfg){
lmMloc = lastModified(mloc);
lmTpl = lastModified(tpl);
res = !found || lmMloc > lmTpl;
//println("tplOutdated <qualifiedModuleName>: <res>; mloc: <lmMloc> \> tpl: <lmTpl>: <lmMloc > lmTpl>, (<mloc>, <tpl>)");
// println("tplOutdated <qualifiedModuleName>: <res>; mloc: <lmMloc> \> tpl: <lmTpl>: <lmMloc > lmTpl>, (<mloc>, <tpl>)");
return res;
} catch _: {
return false;
Expand Down
11 changes: 5 additions & 6 deletions src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,11 @@ str getModuleNameFromAnyLogical(loc l){
tuple[bool, ModuleStatus] importsAndExtendsAreBinaryCompatible(TModel tm, set[MODID] importsAndExtends, ModuleStatus ms){
moduleName = tm.modelName;
physical2logical = invertUnique(tm.logical2physical);

modRequires = { lg | l <- range(tm.useDef),
physical2logical[l]?, lg := physical2logical[l],
moduleName !:= getModuleNameFromAnyLogical(lg) };
modRequires = {lg | l <- range(tm.useDef),
l in physical2logical,
lg := physical2logical[l],
moduleName != getModuleNameFromAnyLogical(lg)
};
provided = {};
if(!isEmpty(modRequires)){
for(m <- importsAndExtends){
Expand All @@ -342,8 +343,6 @@ tuple[bool, ModuleStatus] importsAndExtendsAreBinaryCompatible(TModel tm, set[MO
}
}

//println("<moduleName> requires <modRequires>");

if(isEmpty(modRequires - provided)){
//println("importsAndExtendsAreBinaryCompatible <moduleName>: satisfied");
return <true, ms>;
Expand Down
19 changes: 10 additions & 9 deletions src/org/rascalmpl/compiler/lang/rascalcore/check/TestConfigs.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import lang::rascalcore::check::BasicRascalConfig;
import lang::rascalcore::check::RascalConfig;

import lang::rascalcore::check::ModuleLocations;
import lang::rascalcore::check::TestShared;
// import lang::rascalcore::CompilerPathConfig;

// Duplicate in lang::rascalcore::compile::util::Names, factor out
Expand Down Expand Up @@ -74,10 +75,10 @@ public PathConfig getDefaultTestingPathConfig() {
npc += 1;
snpc = "<npc>";
return pathConfig(
srcs = [ |memory:///test-modules/|, |std:///| ],
bin = |memory:///test-modules/rascal-tests-bin-<snpc>|,
generatedSources = |memory:///test-modules/generated-test-sources-<snpc>|,
generatedResources = |memory:///test-modules/generated-test-resources-<snpc>|,
srcs = [ testModulesRoot, |std:///| ],
bin = testRoot + "rascal-tests-bin-<snpc>",
generatedSources = testRoot + "generated-test-sources-<snpc>",
generatedResources = testRoot + "generated-test-resources-<snpc>",
libs = [ ]
);
}
Expand All @@ -92,10 +93,10 @@ public PathConfig getReleasedStandardLibraryTestingPathConfig() {
npc += 1;
snpc = "<npc>";
return pathConfig(
srcs = [ |memory:///test-modules/| ],
bin = |memory:///test-modules/rascal-tests-bin-<snpc>|,
generatedSources = |memory:///test-modules/generated-test-sources-<snpc>|,
generatedResources = |memory:///test-modules/generated-test-resources-<snpc>|,
srcs = [ testModulesRoot ],
bin = testRoot + "rascal-tests-bin-<snpc>",
generatedSources = testRoot + "generated-test-sources-<snpc>",
generatedResources = testRoot + "generated-test-resources-<snpc>",
libs = [ |lib://rascal| ]
);
}
Expand All @@ -108,7 +109,7 @@ public PathConfig getRascalProjectTestingPathConfig() {
snpc = "<npc>";
return pathConfig(
srcs = [|project://rascal/src/org/rascalmpl/library|],
bin = |memory:///test-modules/rascal-lib-bin-<snpc>|,
bin = testModulesRoot + "rascal-lib-bin-<snpc>",
libs = []
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module lang::rascalcore::check::TestShared

import util::UUID;


public loc testRoot = uuid()[scheme="memory"];
public loc testModulesRoot = testRoot + "src";

Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import String;
import lang::rascalcore::check::ModuleLocations;
import util::FileSystem;
import util::SemVer;
import lang::rascalcore::check::TestShared;
import lang::rascalcore::check::tests::StaticTestingUtils;


// ---- Utilities for test setup ----------------------------------------------
Expand All @@ -57,10 +59,10 @@ data PathConfig(loc generatedResources=|unknown:///|, loc generatedSources=|unkn
data Project
= project(str name, map[str moduleName, str moduleText] modules, PathConfig pcfg);

void clearMemory() { remove(|memory:///|, recursive = true); }
void clearMemory() { remove(testRoot, recursive = true); }

loc projectDir(str pname)
= |memory://<pname>/|;
= testRoot + pname;

loc src(str pname)
= projectDir(pname) + "src/";
Expand Down Expand Up @@ -99,6 +101,9 @@ str writeModule(str mname, str mtext){
throw "Parse error in <msrc>";
}

loc getModuleLoc(str mname, Project pd)
= src(pd.name) + "<mname>.rsc";

PathConfig createPathConfig(str pname){
return pathConfig(
srcs=[src(pname)],
Expand All @@ -111,22 +116,28 @@ PathConfig createPathConfig(str pname){

Project addModule(str mname, str mtext, Project pd){
pd.modules[mname] = writeModule(mname, mtext);
writeFile(src(pd.name) + "<mname>.rsc", pd.modules[mname]);
mloc = getModuleLoc(mname, pd);
writeFile(mloc, pd.modules[mname]);
assert exists(mloc) : "<mloc> does not exist after write";
return pd;
}

Project changeModule(str mname, str mtext, Project pd){
if(!pd.modules[mname]?) throw "Module <mname> does not exist in <pd.name>";

pd.modules[mname] = writeModule(mname, mtext);
writeFile(src(pd.name) + "<mname>.rsc", pd.modules[mname]);
mloc = getModuleLoc(mname, pd);
writeFile(mloc, pd.modules[mname]);
assert exists(mloc) : "<mloc> does not exist after write";
return pd;
}

Project removeSourceOfModule(str mname, Project pd){
if(!pd.modules[mname]?) throw "Cannot remove non-existing module <mname>";
pd.modules = delete(pd.modules, mname);
remove(src(pd.name) + "<mname>.rsc", recursive=true);
mloc = getModuleLoc(mname, pd);
remove(mloc, recursive=true);
assert !exists(mloc): "<mloc> not removed";
return pd;
}

Expand Down Expand Up @@ -533,7 +544,7 @@ test bool incompatibleVersionsOfBinaryLibrary(){

// Important: we do not recompile TP (and thus it will contain the outdated version of IO)

// Update Checks' modification time to make sure it will rechecked
// Update Checks' modification time to make sure it will be rechecked
touch(getRascalModuleLocation("Check", core.pcfg));
// Recompile Check and discover the error
return checkExpectErrors("Check", ["Review of dependencies, reconfiguration or recompilation needed: binary module `TP` depends (indirectly) on incompatible module(s)"], core.pcfg, remove = [rascal, typepal, core]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,29 @@ test bool breakingChange1(){
return expectReChecks(D, ["C", "D"]);
}

test bool fixedErrorsDisappear2() { // ht @toinehartman
clearMemory();
pcfg = getDefaultTestingPathConfig();

mlocs = writeModules("
module ParserBase
");

assert checkModulesOK(mlocs, pathConfig=pcfg) : "Precondition failed: no errors expected!";

// Introduce a type error (import of module that does not exist)
l = writeModule("
module ParserBase

import vis::ParseTree; // module does not exist -\> error

");

assert missingModuleInModule(l, pathConfig=pcfg) : "Precondition failed: expected at least one error, but got none!";

return true;
}

test bool fixedErrorsDisappear() { // ht @toinehartman
clearMemory();
pcfg = getReleasedStandardLibraryTestingPathConfig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ import Relation;
import Set;
import util::Reflective;
import ParseTree;
import util::FileSystem;
import lang::rascalcore::check::RascalConfig;

import lang::rascalcore::check::TestShared;

import lang::rascalcore::check::Checker;
import lang::rascal::\syntax::Rascal;

Expand Down Expand Up @@ -81,14 +84,14 @@ loc composeModule(str stmts){
}

void clearMemory() {
remove(|memory:///test-modules/| recursive = true);
remove(testRoot, recursive = true);
}
str cleanName(str name)
= name[0] == "\\" ? name[1..] : name;

loc writeModule(str moduleText){
<mname, mbody> = extractModuleNameAndBody(moduleText);
mloc = |memory:///test-modules/<cleanName(mname)>.rsc|;
mloc = testModulesRoot + "<cleanName(mname)>.rsc";
writeFile(mloc, moduleText);
return mloc;
}
Expand All @@ -99,10 +102,22 @@ list[loc] writeModules(str modules...)
void removeModule(str mname){
pcfg = getDefaultTestingPathConfig();
name = cleanName(mname);
remove(|memory:///test-modules/<name>.rsc|);
remove(testModulesRoot + "<name>.rsc");
remove(pcfg.generatedResources + "<name>.tpl");
}

void printModules(){
println("\<\<\<\<");
for(f <- find(testRoot, "rsc")){
println("<f> <lastModified(f)>:
'<readFile(f)>");
}
for(f <- find(testRoot, "tpl")){
println("<f>: <lastModified(f)>");
}
println("\>\>\>\>");
}

set[Message] getErrorMessages(ModuleStatus r)
= { m | m <- getAllMessages(r), m is error };

Expand Down
Loading