From 1fc134072c3979771b7eea9a457647851896e8fc Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Mon, 4 Apr 2011 18:40:39 +0400 Subject: [PATCH] Test framework for instructions --- .../inspectionProfiles/profiles_settings.xml | 7 - .../pseudocode/JetControlFlowDataTrace.java | 28 ++-- .../JetControlFlowDataTraceFactory.java | 20 +++ .../JetControlFlowInstructionsGenerator.java | 12 +- .../jet/lang/cfg/pseudocode/Pseudocode.java | 33 +++- .../jet/lang/psi/JetDeclarationWithBody.java | 4 + .../jet/lang/resolve/AnalyzingUtils.java | 9 +- .../jet/lang/resolve/TopDownAnalyzer.java | 81 ++++------ idea/testData/cfg/Basic.instructions | 6 + idea/testData/cfg/Basic.jet | 3 + .../org/jetbrains/jet/JetTestCaseBase.java | 96 +++++++++++ .../jetbrains/jet/cfg/JetControlFlowTest.java | 149 ++++++++++++++++++ .../jet/checkers/JetPsiCheckerTest.java | 44 ++---- .../jetbrains/jet/parsing/JetParsingTest.java | 46 ++---- 14 files changed, 389 insertions(+), 149 deletions(-) delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java create mode 100644 idea/testData/cfg/Basic.instructions create mode 100644 idea/testData/cfg/Basic.jet create mode 100644 idea/tests/org/jetbrains/jet/JetTestCaseBase.java create mode 100644 idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 3b312839bf2..00000000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTrace.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTrace.java index 90e422bdab8..b16a3454c67 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTrace.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTrace.java @@ -1,30 +1,24 @@ package org.jetbrains.jet.lang.cfg.pseudocode; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.JetElement; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - /** * @author abreslav */ -public class JetControlFlowDataTrace { +public interface JetControlFlowDataTrace { - private Map data = new LinkedHashMap(); + JetControlFlowDataTrace EMPTY = new JetControlFlowDataTrace() { + @Override + public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) { + } - public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) { - data.put(element, pseudocode); - } + @Override + public void close() { + } + }; - @Nullable - public Pseudocode getControlFlowData(@NotNull JetElement element) { - return data.get(element); - } + void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode); + void close(); - public Collection getAllData() { - return data.values(); - } } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java new file mode 100644 index 00000000000..e1c946ac086 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java @@ -0,0 +1,20 @@ +package org.jetbrains.jet.lang.cfg.pseudocode; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetElement; + +/** + * @author abreslav + */ +public interface JetControlFlowDataTraceFactory { + JetControlFlowDataTraceFactory EMPTY = new JetControlFlowDataTraceFactory() { + @NotNull + @Override + public JetControlFlowDataTrace createTrace(JetElement element) { + return JetControlFlowDataTrace.EMPTY; + } + }; + + @NotNull + JetControlFlowDataTrace createTrace(JetElement element); +} diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java index dda6a393931..9fd6907e304 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java @@ -28,8 +28,8 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd this.trace = trace; } - private void pushBuilder(JetElement subroutine) { - JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(subroutine); + private void pushBuilder(JetElement scopingElement, JetElement subroutine) { + JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(scopingElement, subroutine); builders.push(worker); builder = worker; } @@ -46,10 +46,10 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd @Override public void enterSubroutine(@NotNull JetElement subroutine, boolean isFunctionLiteral) { if (isFunctionLiteral) { - pushBuilder(builder.getCurrentSubroutine()); + pushBuilder(subroutine, builder.getCurrentSubroutine()); } else { - pushBuilder(subroutine); + pushBuilder(subroutine, subroutine); } builder.enterSubroutine(subroutine, false); } @@ -71,8 +71,8 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd private final Pseudocode pseudocode; private final JetElement currentSubroutine; - private JetControlFlowInstructionsGeneratorWorker(JetElement currentSubroutine) { - this.pseudocode = new Pseudocode(); + private JetControlFlowInstructionsGeneratorWorker(@NotNull JetElement scopingElement, @NotNull JetElement currentSubroutine) { + this.pseudocode = new Pseudocode(scopingElement); this.currentSubroutine = currentSubroutine; } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java index 67e9899ebc5..b157a33e763 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.lang.cfg.pseudocode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.cfg.Label; +import org.jetbrains.jet.lang.psi.JetElement; import java.io.PrintStream; import java.util.*; @@ -50,6 +51,16 @@ public class Pseudocode { private final List instructions = new ArrayList(); private final List labels = new ArrayList(); + private final JetElement correspondingElement; + + public Pseudocode(JetElement correspondingElement) { + this.correspondingElement = correspondingElement; + } + + public JetElement getCorrespondingElement() { + return correspondingElement; + } + public PseudocodeLabel createLabel(String name) { PseudocodeLabel label = new PseudocodeLabel(name); labels.add(label); @@ -146,7 +157,23 @@ public class Pseudocode { return getTargetInstruction(instructions.subList(targetPosition, instructions.size())); } - public void dumpInstructions(@NotNull PrintStream out) { + public void dfsDump(StringBuilder nodes, StringBuilder edges, Map nodeNames) { + dfsDump(nodes, edges, instructions.get(0), nodeNames); + } + + private void dfsDump(StringBuilder nodes, StringBuilder edges, Instruction instruction, Map nodeNames) { + if (nodeNames.containsKey(instruction)) return; + String name = "n" + nodeNames.size(); + nodeNames.put(instruction, name); + nodes.append(name).append(" := ").append(renderName(instruction)); + + } + + private String renderName(Instruction instruction) { + throw new UnsupportedOperationException(); // TODO + } + + public void dumpInstructions(@NotNull StringBuilder out) { List locals = new ArrayList(); for (int i = 0, instructionsSize = instructions.size(); i < instructionsSize; i++) { Instruction instruction = instructions.get(i); @@ -156,10 +183,10 @@ public class Pseudocode { } for (PseudocodeLabel label: labels) { if (label.getTargetInstructionIndex() == i) { - out.println(label.getName() + ":"); + out.append(label.getName()).append(":\n"); } } - out.println(" " + instruction); + out.append(" ").append(instruction).append("\n"); } for (Pseudocode local : locals) { local.dumpInstructions(out); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java b/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java index 072acf8ba84..df4f9c63776 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java @@ -9,4 +9,8 @@ public interface JetDeclarationWithBody { @Nullable JetExpression getBodyExpression(); + + @Nullable + String getName(); } + diff --git a/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java b/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java index 24064d58ce8..d20100828ad 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java @@ -4,6 +4,7 @@ import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.ErrorHandler; import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetNamespace; import org.jetbrains.jet.lang.resolve.java.JavaPackageScope; @@ -16,10 +17,14 @@ import org.jetbrains.jet.lang.types.ModuleDescriptor; public class AnalyzingUtils { public static BindingContext analyzeFile(@NotNull JetFile file, @NotNull ErrorHandler errorHandler) { JetNamespace rootNamespace = file.getRootNamespace(); - return analyzeNamespace(rootNamespace, errorHandler); + return analyzeNamespace(rootNamespace, errorHandler, JetControlFlowDataTraceFactory.EMPTY); } public static BindingContext analyzeNamespace(@NotNull JetNamespace namespace, @NotNull ErrorHandler errorHandler) { + return analyzeNamespace(namespace, errorHandler, JetControlFlowDataTraceFactory.EMPTY); + } + + public static BindingContext analyzeNamespace(@NotNull JetNamespace namespace, @NotNull ErrorHandler errorHandler, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) { Project project = namespace.getProject(); JetSemanticServices semanticServices = JetSemanticServices.createSemanticServices(project, errorHandler); @@ -33,7 +38,7 @@ public class AnalyzingUtils { scope.importScope(new JavaPackageScope("", null, javaSemanticServices)); scope.importScope(new JavaPackageScope("java.lang", null, javaSemanticServices)); - new TopDownAnalyzer(semanticServices, bindingTraceContext).process(scope, namespace); + new TopDownAnalyzer(semanticServices, bindingTraceContext, flowDataTraceFactory).process(scope, namespace); return bindingTraceContext; } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index c7a595474d9..94803998f91 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -4,10 +4,7 @@ import com.intellij.openapi.application.ApplicationManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.cfg.JetControlFlowProcessor; -import org.jetbrains.jet.lang.cfg.pseudocode.Instruction; -import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTrace; -import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowInstructionsGenerator; -import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode; +import org.jetbrains.jet.lang.cfg.pseudocode.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.*; @@ -30,12 +27,22 @@ public class TopDownAnalyzer { private final ClassDescriptorResolver classDescriptorResolver; private final BindingTrace trace; private final JetTypeInferrer typeInferrer; + private final JetControlFlowDataTraceFactory flowDataTraceFactory; + + public TopDownAnalyzer(JetSemanticServices semanticServices, @NotNull BindingTrace bindingTrace, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) { + this.semanticServices = semanticServices; + this.classDescriptorResolver = new ClassDescriptorResolver(semanticServices, bindingTrace); + this.trace = bindingTrace; + this.typeInferrer = semanticServices.getTypeInferrer(trace); + this.flowDataTraceFactory = flowDataTraceFactory; + } public TopDownAnalyzer(JetSemanticServices semanticServices, @NotNull BindingTrace bindingTrace) { this.semanticServices = semanticServices; this.classDescriptorResolver = new ClassDescriptorResolver(semanticServices, bindingTrace); this.trace = bindingTrace; this.typeInferrer = semanticServices.getTypeInferrer(trace); + this.flowDataTraceFactory = JetControlFlowDataTraceFactory.EMPTY; } public void process(@NotNull JetScope outerScope, @NotNull JetDeclaration declaration) { @@ -233,47 +240,6 @@ public class TopDownAnalyzer { FunctionDescriptor descriptor = classDescriptorResolver.resolveFunctionDescriptor(declaringScope.getContainingDeclaration(), declaringScope, function); declaringScope.addFunctionDescriptor(descriptor); functions.put(function, descriptor); - - JetExpression bodyExpression = function.getBodyExpression(); - if (bodyExpression != null) { - JetControlFlowDataTrace controlFlowDataTrace = new JetControlFlowDataTrace(); - JetControlFlowInstructionsGenerator instructionsGenerator = new JetControlFlowInstructionsGenerator(controlFlowDataTrace); - new JetControlFlowProcessor(semanticServices, trace, instructionsGenerator).generate(function, bodyExpression); - if (!ApplicationManager.getApplication().isUnitTestMode()) { - try { - File workDirectory = new File(System.getProperty("user.home") + "/work/"); - workDirectory.mkdirs(); - File target = new File(workDirectory, "cfg.dot"); - - PrintStream out = new PrintStream(target); - out.println("digraph " + function.getName() + " {"); - Collection pseudocodes = controlFlowDataTrace.getAllData(); - int[] count = new int[1]; - Map nodeToName = new HashMap(); - for (Pseudocode pseudocode : pseudocodes) { - pseudocode.postProcess(); - System.out.println("-------------"); - pseudocode.dumpInstructions(System.out); - System.out.println("-------------"); - pseudocode.dumpNodes(out, count, nodeToName); - } - int i = 0; - for (Pseudocode pseudocode : pseudocodes) { - out.println("subgraph cluster_" + i + " {\n" + - "label=\"f" + i + "\";\n" + - "color=blue;\n"); - pseudocode.dumpEdges(out, count, nodeToName); - out.println("}"); - i++; - } - out.println("}"); - out.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } - - } - } } private void processProperty(WritableScope declaringScope, JetProperty property) { @@ -290,10 +256,10 @@ public class TopDownAnalyzer { private void resolveBehaviorDeclarationBodies() { for (Map.Entry entry : functions.entrySet()) { - JetDeclaration declarations = entry.getKey(); + JetDeclaration declaration = entry.getKey(); FunctionDescriptor descriptor = entry.getValue(); - WritableScope declaringScope = declaringScopes.get(declarations); + WritableScope declaringScope = declaringScopes.get(declaration); assert declaringScope != null; WritableScope parameterScope = semanticServices.createWritableScope(declaringScope, descriptor); @@ -304,15 +270,28 @@ public class TopDownAnalyzer { parameterScope.addPropertyDescriptor(valueParameterDescriptor); } - assert declarations instanceof JetFunction || declarations instanceof JetConstructor; - JetExpression bodyExpression = ((JetDeclarationWithBody) declarations).getBodyExpression(); + assert declaration instanceof JetFunction || declaration instanceof JetConstructor; + JetDeclarationWithBody declarationWithBody = (JetDeclarationWithBody) declaration; + JetExpression bodyExpression = declarationWithBody.getBodyExpression(); + if (bodyExpression != null) { - resolveExpression(parameterScope, bodyExpression, true); + JetControlFlowDataTrace controlFlowDataTrace = flowDataTraceFactory.createTrace(declaration); + JetControlFlowInstructionsGenerator instructionsGenerator = new JetControlFlowInstructionsGenerator(controlFlowDataTrace); + new JetControlFlowProcessor(semanticServices, trace, instructionsGenerator).generate(declaration, bodyExpression); + controlFlowDataTrace.close(); + + boolean preferBlock = true; + if (declaration instanceof JetFunction) { + JetFunction jetFunction = (JetFunction) declaration; + preferBlock = jetFunction.hasBlockBody(); + } + + resolveExpression(parameterScope, bodyExpression, preferBlock, controlFlowDataTrace); } } } - private void resolveExpression(@NotNull JetScope scope, JetExpression expression, boolean preferBlock) { + private void resolveExpression(@NotNull JetScope scope, JetExpression expression, boolean preferBlock, JetControlFlowDataTrace controlFlowDataTrace) { typeInferrer.getType(scope, expression, preferBlock); } diff --git a/idea/testData/cfg/Basic.instructions b/idea/testData/cfg/Basic.instructions new file mode 100644 index 00000000000..457dca2839b --- /dev/null +++ b/idea/testData/cfg/Basic.instructions @@ -0,0 +1,6 @@ +l0: + + r(a) +l1: + +===================== diff --git a/idea/testData/cfg/Basic.jet b/idea/testData/cfg/Basic.jet new file mode 100644 index 00000000000..60009e6397b --- /dev/null +++ b/idea/testData/cfg/Basic.jet @@ -0,0 +1,3 @@ +fun f(a : Boolean) { + a +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/JetTestCaseBase.java b/idea/tests/org/jetbrains/jet/JetTestCaseBase.java new file mode 100644 index 00000000000..c086a793366 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/JetTestCaseBase.java @@ -0,0 +1,96 @@ +package org.jetbrains.jet; + +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.openapi.application.PathManager; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileFilter; +import java.io.FilenameFilter; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author abreslav + */ +public abstract class JetTestCaseBase extends LightDaemonAnalyzerTestCase { + + private String dataPath; + protected final String name; + + public JetTestCaseBase(String dataPath, String name) { + this.dataPath = dataPath; + this.name = name; + } + + @Override + protected String getTestDataPath() { + return getTestDataPathBase(); + } + + protected static String getTestDataPathBase() { + return getHomeDirectory() + "/idea/testData"; + } + + private static String getHomeDirectory() { + return new File(PathManager.getResourceRoot(JetTestCaseBase.class, "/org/jetbrains/jet/JetTestCaseBase.class")).getParentFile().getParentFile().getParent(); + } + + @Override + public String getName() { + return "test" + name; + } + + @Override + protected void runTest() throws Throwable { + doTest(getTestFilePath(), false, false); + } + + @NotNull + protected String getTestFilePath() { + return dataPath + name + ".jet"; + } + + public interface NamedTestFactory { + @NotNull Test createTest(@NotNull String dataPath, @NotNull String name); + } + + @NotNull + public static TestSuite suiteForDirectory(String baseDataDir, @NotNull final String dataPath, boolean recursive, @NotNull NamedTestFactory factory) { + TestSuite suite = new TestSuite(dataPath); + final String extension = ".jet"; + FilenameFilter extensionFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(extension); + } + }; + File dir = new File(baseDataDir + dataPath); + FileFilter dirFilter = new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isDirectory(); + } + }; + if (recursive) { + File[] files = dir.listFiles(dirFilter); + assert files != null : dir; + List subdirs = Arrays.asList(files); + Collections.sort(subdirs); + for (File subdir : subdirs) { + suite.addTest(suiteForDirectory(baseDataDir, dataPath + "/" + subdir.getName(), recursive, factory)); + } + } + List files = Arrays.asList(dir.listFiles(extensionFilter)); + Collections.sort(files); + for (File file : files) { + String fileName = file.getName(); + assert fileName != null; + suite.addTest(factory.createTest(dataPath, fileName.substring(0, fileName.length() - extension.length()))); + } + return suite; + } +} diff --git a/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java b/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java new file mode 100644 index 00000000000..82c43840605 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java @@ -0,0 +1,149 @@ +/* + * @author max + */ +package org.jetbrains.jet.cfg; + +import com.intellij.openapi.util.io.FileUtil; +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetTestCaseBase; +import org.jetbrains.jet.lang.ErrorHandler; +import org.jetbrains.jet.lang.cfg.pseudocode.Instruction; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTrace; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; +import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode; +import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetNamedDeclaration; +import org.jetbrains.jet.lang.resolve.AnalyzingUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +public class JetControlFlowTest extends JetTestCaseBase { + static { + System.setProperty("idea.platform.prefix", "Idea"); + } + + public JetControlFlowTest(String dataPath, String name) { + super(dataPath, name); + } + + @Override + protected void runTest() throws Throwable { + configureByFile(getTestFilePath()); + JetFile file = (JetFile) getFile(); + + AnalyzingUtils.analyzeNamespace(file.getRootNamespace(), ErrorHandler.THROW_EXCEPTION, new JetControlFlowDataTraceFactory() { + @NotNull + @Override + public JetControlFlowDataTrace createTrace(JetElement element) { + return new JetControlFlowDataTrace() { + private final Map data = new LinkedHashMap(); + + @Override + public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) { + data.put(element, pseudocode); + } + + @Override + public void close() { + try { + try { + processCFData(name); + } catch (AssertionFailedError e) { + dumpDot(name, data.values()); + throw e; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void processCFData(String name) throws IOException { + Collection pseudocodes = data.values(); + for (Pseudocode pseudocode : pseudocodes) { + pseudocode.postProcess(); + } + + StringBuilder instructionDump = new StringBuilder(); + for (Pseudocode pseudocode : pseudocodes) { + pseudocode.dumpInstructions(instructionDump); + instructionDump.append("=====================\n"); + } + + String expectedInstructionsFileName = getTestDataPath() + "/" + getTestFilePath().replace(".jet", ".instructions"); + File expectedInstructionsFile = new File(expectedInstructionsFileName); + if (!expectedInstructionsFile.exists()) { + FileUtil.writeToFile(expectedInstructionsFile, instructionDump.toString()); + fail("No expected instructions for " + name + " generated result is written into " + expectedInstructionsFileName); + } + String expectedInstructions = FileUtil.loadFile(expectedInstructionsFile); + + assertEquals(expectedInstructions, instructionDump.toString()); + +// StringBuilder graphDump = new StringBuilder(); +// for (Pseudocode pseudocode : pseudocodes) { +// topOrderDump(pseudocode.) +// } + } + }; + } + }); + } + + private void dumpDot(String name, Collection pseudocodes) throws FileNotFoundException { + String graphFileName = getTestDataPath() + "/" + getTestFilePath().replace(".jet", ".dot"); + File target = new File(graphFileName); + + PrintStream out = new PrintStream(target); + + out.println("digraph " + name + " {"); + int[] count = new int[1]; + Map nodeToName = new HashMap(); + for (Pseudocode pseudocode : pseudocodes) { + pseudocode.dumpNodes(out, count, nodeToName); + } + int i = 0; + for (Pseudocode pseudocode : pseudocodes) { + String label; + JetElement correspondingElement = pseudocode.getCorrespondingElement(); + if (correspondingElement instanceof JetNamedDeclaration) { + JetNamedDeclaration namedDeclaration = (JetNamedDeclaration) correspondingElement; + label = namedDeclaration.getName(); + } + else { + label = "anonymous_" + i; + } + out.println("subgraph cluster_" + i + " {\n" + + "label=\"" + label + "\";\n" + + "color=blue;\n"); + pseudocode.dumpEdges(out, count, nodeToName); + out.println("}"); + i++; + } + out.println("}"); + out.close(); + } + + public static TestSuite suite() { + TestSuite suite = new TestSuite(); + suite.addTest(JetTestCaseBase.suiteForDirectory(getTestDataPathBase(), "/cfg/", true, new JetTestCaseBase.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, @NotNull String name) { + return new JetControlFlowTest(dataPath, name); + } + })); + return suite; + } + +} diff --git a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java index d59db8f7704..349989c7fed 100644 --- a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java @@ -1,38 +1,28 @@ package org.jetbrains.jet.checkers; -import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; -import com.intellij.openapi.application.PathManager; -import org.jetbrains.jet.parsing.JetParsingTest; - -import java.io.File; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetTestCaseBase; /** * @author abreslav */ -public class JetPsiCheckerTest extends LightDaemonAnalyzerTestCase { +public class JetPsiCheckerTest extends JetTestCaseBase { - @Override - protected String getTestDataPath() { - return getHomeDirectory() + "/idea/testData"; + public JetPsiCheckerTest(String dataPath, String name) { + super(dataPath, name); } - private static String getHomeDirectory() { - return new File(PathManager.getResourceRoot(JetParsingTest.class, "/org/jetbrains/jet/parsing/JetParsingTest.class")).getParentFile().getParentFile().getParent(); - } - - public void testProperties() throws Exception { - doTest("/checker/Properties.jet", true, true); - } - - public void testBinaryCallsOnNullableValues() throws Exception { - doTest("/checker/BinaryCallsOnNullableValues.jet", true, true); - } - - public void testQualifiedThis() throws Exception { - doTest("/checker/QualifiedThis.jet", true, true); - } - - public void testBreakContinue() throws Exception { - doTest("/checker/BreakContinue.jet", true, true); + public static Test suite() { + TestSuite suite = new TestSuite(); + suite.addTest(JetTestCaseBase.suiteForDirectory(getTestDataPathBase(), "/checker/", true, new JetTestCaseBase.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, @NotNull String name) { + return new JetPsiCheckerTest(dataPath, name); + } + })); + return suite; } } diff --git a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java index 6706c6bf2a9..9a117cc015b 100644 --- a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java +++ b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java @@ -9,20 +9,18 @@ import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.testFramework.ParsingTestCase; +import junit.framework.Test; import junit.framework.TestSuite; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetTestCaseBase; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetVisitor; import java.io.File; -import java.io.FileFilter; -import java.io.FilenameFilter; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; public class JetParsingTest extends ParsingTestCase { static { @@ -101,40 +99,16 @@ public class JetParsingTest extends ParsingTestCase { public static TestSuite suite() { TestSuite suite = new TestSuite(); - suite.addTest(suiteForDirectory("/", false)); - suite.addTest(suiteForDirectory("examples", true)); - return suite; - } - - private static TestSuite suiteForDirectory(final String dataPath, boolean recursive) { - TestSuite suite = new TestSuite(dataPath); - final String extension = ".jet"; - FilenameFilter extensionFilter = new FilenameFilter() { + JetTestCaseBase.NamedTestFactory factory = new JetTestCaseBase.NamedTestFactory() { + @NotNull @Override - public boolean accept(File dir, String name) { - return name.endsWith(extension); + public Test createTest(@NotNull String dataPath, @NotNull String name) { + return new JetParsingTest(dataPath, name); } }; - File dir = new File(getTestDataDir() + "/psi/" + dataPath); - FileFilter dirFilter = new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.isDirectory(); - } - }; - if (recursive) { - List subdirs = Arrays.asList(dir.listFiles(dirFilter)); - Collections.sort(subdirs); - for (File subdir : subdirs) { - suite.addTest(suiteForDirectory(dataPath + "/" + subdir.getName(), recursive)); - } - } - List files = Arrays.asList(dir.listFiles(extensionFilter)); - Collections.sort(files); - for (File file : files) { - String fileName = file.getName(); - suite.addTest(new JetParsingTest(dataPath, fileName.substring(0, fileName.length() - extension.length()))); - } + String prefix = JetParsingTest.getTestDataDir() + "/psi/"; + suite.addTest(JetTestCaseBase.suiteForDirectory(prefix, "/", false, factory)); + suite.addTest(JetTestCaseBase.suiteForDirectory(prefix, "examples", true, factory)); return suite; }