From 41afe7e7a394fc10b70da274fffe5463e30b90f0 Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Tue, 24 Dec 2019 15:16:43 +0100
Subject: [PATCH] add two very simple tests (one fails, because inheritance is
 not yet considered)

---
 scope4j/build.gradle                          |  8 ++
 .../main/java/org/extendj/ScopeAnalysis.java  | 80 ++++++++++++++-----
 .../java/org/extendj/ScopeAnalysisTest.java   | 21 +++++
 .../java/org/extendj/SimpleScopeTest.java     | 29 +++++++
 .../org/extendj/SuperclassFieldsTest.java     | 26 ++++++
 scope4j/src/test/resources/simple/ClassA.java | 42 ++++++++++
 .../resources/superclassFields/ClassA.java    | 12 +++
 .../resources/superclassFields/ClassB.java    | 26 ++++++
 8 files changed, 222 insertions(+), 22 deletions(-)
 create mode 100644 scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
 create mode 100644 scope4j/src/test/java/org/extendj/SimpleScopeTest.java
 create mode 100644 scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
 create mode 100644 scope4j/src/test/resources/simple/ClassA.java
 create mode 100644 scope4j/src/test/resources/superclassFields/ClassA.java
 create mode 100644 scope4j/src/test/resources/superclassFields/ClassB.java

diff --git a/scope4j/build.gradle b/scope4j/build.gradle
index 74c3ca8..cf6e3a0 100644
--- a/scope4j/build.gradle
+++ b/scope4j/build.gradle
@@ -23,6 +23,10 @@ idea {
     }
 }
 
+test {
+    useJUnitPlatform {}
+}
+
 sourceSets.main {
     java {
 
@@ -37,6 +41,10 @@ sourceSets.main {
 }
 
 dependencies {
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
+    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2'
+    testCompile 'org.junit.platform:junit-platform-runner:1.4.2'
+    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2'
 }
 
 jastadd {
diff --git a/scope4j/src/main/java/org/extendj/ScopeAnalysis.java b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
index 9ac9889..333ad33 100644
--- a/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
+++ b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
@@ -18,6 +18,12 @@ public class ScopeAnalysis extends Frontend {
   }
 
 
+  public Program getProgram() {
+    return program;
+  }
+
+  private Program program;
+
   /**
    * Entry point for the Java checker.
    *
@@ -28,13 +34,24 @@ public class ScopeAnalysis extends Frontend {
     List<String> arguments = new ArrayList<>(Arrays.asList(args));
 
     boolean debug = arguments.isEmpty() || arguments.remove("--debug");
+    boolean tree = arguments.remove("--tree");
+    boolean warnings = arguments.remove("--warnings");
 
     if (arguments.size() > 1) {
-      System.out.println("usage: ScopeAnalysis [--debug] <directory with java files>");
+      System.out.println("usage: ScopeAnalysis [--debug] [--tree] [--warnings] <directory with java files>");
       System.exit(-1);
     }
     String path = arguments.isEmpty() ? "../testprograms/simpleScope" : arguments.get(arguments.size() - 1);
 
+    if (debug) {
+      new ScopeAnalysis().analyze(path, tree, warnings);
+    } else {
+      new ScopeAnalysis().analyzeTimed(path);
+    }
+
+  }
+
+  public void analyzeTimed(String path) {
     try {
       List<String> files = Files.walk(Paths.get(path))
           .filter(Files::isRegularFile)
@@ -43,37 +60,17 @@ public class ScopeAnalysis extends Frontend {
       // measure the time (with parsing) from here
       long startMeasurementTime = System.nanoTime();
 
-      Program program = new ScopeAnalysis().readProgram(files);
+      program = readProgram(files);
 
       // measure the time (without parsing) from here
       long startGenerationTime = System.nanoTime();
 
       ScopeTree scopeTree = program.scopeTree();
 
-      if (debug) {
-        scopeTree.printAST();
-
-        System.out.println("\nExtendJ found the following problems:");
-        for (CompilationUnit unit : program.getCompilationUnitList()) {
-          for (Problem problem : unit.problems()) {
-            System.out.println(problem);
-          }
-        }
-        System.out.println();
-      }
-
       long startAnalysisTime = System.nanoTime();
 
       Set<VariableShadowFinding> findings = scopeTree.variableShadowings();
 
-      if (debug) {
-        System.out.println("\nScope4J found the following problems:");
-        for (VariableShadowFinding finding : findings) {
-          System.out.println(finding);
-        }
-        System.out.println();
-      }
-
       // measure the time until here
       long endTime = System.nanoTime();
 
@@ -100,6 +97,45 @@ public class ScopeAnalysis extends Frontend {
     }
   }
 
+
+  public Set<VariableShadowFinding> analyze(String path, boolean tree, boolean warnings) {
+    try {
+      List<String> files = Files.walk(Paths.get(path))
+          .filter(Files::isRegularFile)
+          .filter(x -> x.getFileName().toString().endsWith(".java")).map(Path::toString).collect(Collectors.toList());
+
+      program = readProgram(files);
+
+      ScopeTree scopeTree = program.scopeTree();
+
+      if (tree) {
+        scopeTree.printAST();
+      }
+
+      if (warnings) {
+        System.out.println("\nExtendJ found the following problems:");
+        for (CompilationUnit unit : program.getCompilationUnitList()) {
+          for (Problem problem : unit.problems()) {
+            System.out.println(problem);
+          }
+        }
+        System.out.println();
+      }
+
+      Set<VariableShadowFinding> findings = scopeTree.variableShadowings();
+
+      System.out.println("\nScope4J found the following problems:");
+      for (VariableShadowFinding finding : findings) {
+        System.out.println(finding);
+      }
+      System.out.println();
+
+      return findings;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   private Program readProgram(Collection<String> files) throws IOException {
 
 
diff --git a/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java b/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
new file mode 100644
index 0000000..10bdd1e
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
@@ -0,0 +1,21 @@
+package org.extendj;
+
+import org.extendj.ast.Declaration;
+import org.extendj.ast.VariableShadowFinding;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+public abstract class ScopeAnalysisTest {
+  static void assertShadow(Set<VariableShadowFinding> findings, String name, int shadowerLine, int shadowedLine) {
+    for (VariableShadowFinding finding : findings) {
+      Declaration shadower = finding.getShadower();
+      Declaration shadowed = finding.getShadowed();
+      if (shadowed.getName().equals(name) && shadowed.lineNumber() == shadowedLine && shadower.lineNumber() == shadowerLine) {
+        return;
+      }
+    }
+    Assertions.fail("No finding found for name '" + name + "' in lines " + shadowerLine + " > " + shadowedLine);
+  }
+}
diff --git a/scope4j/src/test/java/org/extendj/SimpleScopeTest.java b/scope4j/src/test/java/org/extendj/SimpleScopeTest.java
new file mode 100644
index 0000000..c11b502
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/SimpleScopeTest.java
@@ -0,0 +1,29 @@
+package org.extendj;
+
+import org.extendj.ast.VariableShadowFinding;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+public class SimpleScopeTest extends ScopeAnalysisTest {
+
+  @Test
+  void test() {
+
+    ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
+    Set<VariableShadowFinding> findings = scopeAnalysis.analyze("src/test/resources/simple", false, false);
+
+
+    Assertions.assertEquals(5, findings.size());
+
+    assertShadow(findings, "localVarA", 19, 11);
+    assertShadow(findings, "localVarB", 33, 12);
+    assertShadow(findings, "localVarC", 24, 13);
+    assertShadow(findings, "fieldA", 29, 3);
+    assertShadow(findings, "fieldB", 36, 4);
+
+
+  }
+
+}
diff --git a/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java b/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
new file mode 100644
index 0000000..3dd2b9e
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
@@ -0,0 +1,26 @@
+package org.extendj;
+
+import org.extendj.ast.VariableShadowFinding;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+public class SuperclassFieldsTest extends ScopeAnalysisTest {
+
+  @Test
+  void test() {
+
+    ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
+    Set<VariableShadowFinding> findings = scopeAnalysis.analyze("src/test/resources/superclassFields", false, false);
+
+
+    assertShadow(findings, "fieldC", 19, 3);
+    assertShadow(findings, "fieldB", 21, 2);
+    assertShadow(findings, "fieldB", 2, 4);
+
+    Assertions.assertEquals(3, findings.size());
+
+  }
+
+}
diff --git a/scope4j/src/test/resources/simple/ClassA.java b/scope4j/src/test/resources/simple/ClassA.java
new file mode 100644
index 0000000..a066a2a
--- /dev/null
+++ b/scope4j/src/test/resources/simple/ClassA.java
@@ -0,0 +1,42 @@
+public abstract class ClassA {
+
+  int fieldA;
+  int fieldB;
+
+  public ClassA(int constructorParameterA) {
+    int localConstructorVarA = 0;
+  }
+
+  public void methodNameA(int parameterA) {
+    int localVarA = 1;
+    int localVarB = 1;
+    int localVarC = 1;
+
+    {
+      int localVarInBlockA = 2;
+
+      // this is shadowing (and forbidden)
+      int localVarA = 3;
+    }
+
+    class Local {
+      {
+        for (int localVarC = 0; localVarC < 10; localVarC++) System.out.println(localVarC);
+      }
+    }
+
+    // this is shadowing (over two levels, not forbidden)
+    int fieldA;
+
+    try (
+        // this is forbidden
+        java.util.zip.ZipFile localVarB = new java.util.zip.ZipFile("zipFileName");
+
+        // this is okay
+        java.io.BufferedWriter fieldB = java.nio.file.Files.newBufferedWriter(null)
+    ) { /* do stuff */ } catch (java.io.IOException e) {/* do stuff */}
+  }
+
+  // this does not appear as a scope (and, more importantly, the parameters are not added anywhere else)
+  public abstract void methodNameB(int parameterForAbstractMethodB);
+}
diff --git a/scope4j/src/test/resources/superclassFields/ClassA.java b/scope4j/src/test/resources/superclassFields/ClassA.java
new file mode 100644
index 0000000..675b936
--- /dev/null
+++ b/scope4j/src/test/resources/superclassFields/ClassA.java
@@ -0,0 +1,12 @@
+public abstract class ClassA {
+​
+  int fieldA;
+  int fieldB;
+
+  void m();
+
+  void n() {
+    //...
+  }
+​
+}
diff --git a/scope4j/src/test/resources/superclassFields/ClassB.java b/scope4j/src/test/resources/superclassFields/ClassB.java
new file mode 100644
index 0000000..dbd482e
--- /dev/null
+++ b/scope4j/src/test/resources/superclassFields/ClassB.java
@@ -0,0 +1,26 @@
+public class ClassB extends ClassA {
+  int fieldB;
+  int fieldC;
+
+  @Override
+  void m() {
+    //Overridden..
+  }
+
+  void n() {
+    //not overriden
+  }
+
+  void n(int value) {
+    //polymorphic
+  }
+
+  class ClassC {
+    int fieldC;
+
+    public ClassC(int fieldB) {
+      fieldC = fieldB;
+    }
+  }
+​
+}
-- 
GitLab