Просмотр исходного кода

Merge pull request #137 from jplag/104-fix-cli

Fix CLI and add testing capabilities
Sebastian Hahner 5 лет назад
Родитель
Сommit
bc88178502

+ 40 - 13
jplag/src/main/java/jplag/CLI.java

@@ -15,20 +15,22 @@ import net.sourceforge.argparse4j.inf.Namespace;
 
 /**
  * Command line interface class, allows using via command line.
+ *
  * @see CLI#main(String[])
  */
 public class CLI {
-    
+
     private static final String DESCRIPTION = "JPlag - Detecting Software Plagiarism";
     private static final String PROGRAM_NAME = "jplag";
+
+    // TODO SH: Replace verbosity when integrating a real logging library
     private static final String[] verbosityOptions = {"parser", "quiet", "long", "details"};
-    private static final String[] languageOptions = new String[] {"java_1_1", "java_1_2", "java_1_5", "java_1_5_dm", "java_1_7", "java_1_9",
-            "python_3", "c_cpp", "c_sharp", "char", "text", "scheme"};
-    
+
     private final ArgumentParser parser;
 
     /**
      * Main class for using JPlag via the CLI.
+     *
      * @param args are the CLI arguments that will be passed to JPlag.
      */
     public static void main(String[] args) {
@@ -53,21 +55,27 @@ public class CLI {
      */
     public CLI() {
         parser = ArgumentParsers.newFor(PROGRAM_NAME).build().defaultHelp(true).description(DESCRIPTION);
+
+        // TODO SH: Fix code duplication of CLI arguments
         parser.addArgument("rootDir").help("The root-directory that contains all submissions");
-        parser.addArgument("-l").choices(languageOptions).setDefault("java_1_9").help("Select the language to parse the submissions");
+        parser.addArgument("-l")
+                .choices(LanguageOption.getAllDisplayNames())
+                .setDefault(LanguageOption.getDefault().getDisplayName())
+                .help("Select the language to parse the submissions");
         parser.addArgument("-bc").help("Name of the directory which contains the base code (common framework)");
         parser.addArgument("-v").choices(verbosityOptions).setDefault("quiet").help("Verbosity");
         parser.addArgument("-d").help("(Debug) parser. Non-parsable files will be stored").action(storeTrue());
         parser.addArgument("-S").help("Look in directories <root-dir>/*/<dir> for programs");
         parser.addArgument("-p").help("comma-separated list of all filename suffixes that are included");
-        parser.addArgument("-x").help("All files named in <file> will be ignored");
+        parser.addArgument("-x").help("All files named in this file will be ignored in the comparison (line-separated list)");
         parser.addArgument("-t").help("Tune the sensitivity of the comparison. A smaller <n> increases the sensitivity");
-        parser.addArgument("-s").help("Similarity Threshold: all matches above this threshold will be saved");
-        parser.addArgument("-r").setDefault("result").help("Name of directory in which the web pages will be stored");
+        parser.addArgument("-m").setDefault(0f).help("Match similarity Threshold [0-100]: all matches above this threshold will be saved");
+        parser.addArgument("-r").setDefault("result").help("Name of directory in which the comparison results will be stored");
     }
 
     /**
      * Parses an array of argument strings.
+     *
      * @param args is the array to parse.
      * @return the parsed arguments in a {@link Namespace} format.
      */
@@ -83,27 +91,46 @@ public class CLI {
 
     /**
      * Builds a options instance from parsed arguments.
+     *
      * @param namespace encapsulates the parsed arguments in a {@link Namespace} format.
      * @return the newly built options.F
      */
     public JPlagOptions buildOptionsFromArguments(Namespace namespace) {
         String fileSuffixString = namespace.getString("p");
-        String[] fileSuffixes = new String[] {};
+        String[] fileSuffixes = new String[]{};
         if (fileSuffixString != null) {
             fileSuffixes = fileSuffixString.replaceAll("\\s+", "").split(",");
         }
-        LanguageOption language = LanguageOption.fromOption(namespace.getString("l"));
+        LanguageOption language = LanguageOption.fromDisplayName(namespace.getString("l"));
         Verbosity verbosity = Verbosity.fromOption(namespace.getString("v"));
 
         JPlagOptions options = new JPlagOptions(namespace.getString("rootDir"), language);
-        options.setBaseCodeSubmissionName("base-code");
+        options.setBaseCodeSubmissionName(namespace.getString("bc"));
         options.setVerbosity(verbosity);
         options.setDebugParser(namespace.getBoolean("d"));
         options.setSubdirectoryName(namespace.getString("S"));
         options.setFileSuffixes(fileSuffixes);
         options.setExclusionFileName(namespace.getString("x"));
-        options.setMinTokenMatch(namespace.getInt("t"));
-        options.setSimilarityThreshold(namespace.getFloat("s"));
+
+        String minTokenMatch = namespace.getString("t");
+        if (minTokenMatch != null) {
+            try {
+                options.setMinTokenMatch(Integer.parseInt(minTokenMatch));
+            } catch (NumberFormatException e) {
+                System.out.println("Illegal comparison sensitivity. Ignoring input and taking language default value.");
+            }
+        }
+
+        String similarityThreshold = namespace.getString("m");
+        if (similarityThreshold != null) {
+            try {
+                options.setSimilarityThreshold(Float.parseFloat(similarityThreshold));
+            } catch (NumberFormatException e) {
+                System.out.println("Illegal similarity threshold. Taking 0 as default value.");
+                options.setSimilarityThreshold(0);
+            }
+        }
+
         return options;
     }
 }

+ 4 - 2
jplag/src/main/java/jplag/JPlag.java

@@ -117,8 +117,10 @@ public class JPlag implements ProgramI {
      */
     public boolean hasValidSuffix(File file) {
         String[] validSuffixes = options.getFileSuffixes();
+
+        // This is the case if either the language frontends or the CLI did not set the valid suffixes array in options
         if (validSuffixes == null || validSuffixes.length == 0) {
-            return true; // TODO TS: Why does this return true if the valid suffixes are not set?
+            return true;
         }
         return Arrays.stream(validSuffixes).anyMatch(suffix -> file.getName().endsWith(suffix));
     }
@@ -380,7 +382,7 @@ public class JPlag implements ProgramI {
     /**
      * Parse all given submissions.
      */
-    private void parseSubmissions(Vector<Submission> submissions) throws ExitException {
+    private void parseSubmissions(Vector<Submission> submissions) {
         if (submissions == null) {
             System.out.println("Nothing to parse!");
             return;

+ 38 - 29
jplag/src/main/java/jplag/options/LanguageOption.java

@@ -1,25 +1,31 @@
 package jplag.options;
 
+import jplag.java19.Language;
+
+import java.util.Arrays;
+
 /**
  * The available languages.
  */
 public enum LanguageOption {
-    JAVA_1_1("jplag.javax.Language"),
-    JAVA_1_2("jplag.java.Language"),
-    JAVA_1_5("jplag.java15.Language"),
-    JAVA_1_5_DM("jplag.java15.LanguageWithDelimitedMethods"),
-    JAVA_1_7("jplag.java17.Language"),
-    JAVA_1_9("jplag.java19.Language"),
-    PYTHON_3("jplag.python3.Language"),
-    C_CPP("jplag.cpp.Language"),
-    C_SHARP("jplag.csharp.Language"),
-    CHAR("jplag.chars.Language"),
-    TEXT("jplag.text.Language"),
-    SCHEME("jplag.scheme.Language");
+    JAVA_1_1("java1", "jplag.javax.Language"),
+    JAVA_1_2("java2", "jplag.java.Language"),
+    JAVA_1_5("java5", "jplag.java15.Language"),
+    JAVA_1_5_DM("java5dm", "jplag.java15.LanguageWithDelimitedMethods"),
+    JAVA_1_7("java7", "jplag.java17.Language"),
+    JAVA_1_9("java9", "jplag.java19.Language"),
+    PYTHON_3("python3", "jplag.python3.Language"),
+    C_CPP("cpp", "jplag.cpp.Language"),
+    C_SHARP("csharp", "jplag.csharp.Language"),
+    CHAR("char", "jplag.chars.Language"),
+    TEXT("text", "jplag.text.Language"),
+    SCHEME("scheme", "jplag.scheme.Language");
 
     private final String classPath;
+    private final String displayName;
 
-    LanguageOption(String classPath) {
+    LanguageOption(String displayName, String classPath) {
+        this.displayName = displayName;
         this.classPath = classPath;
     }
 
@@ -27,21 +33,24 @@ public enum LanguageOption {
         return this.classPath;
     }
 
-    public static LanguageOption fromOption(String optionName) {
-        switch(optionName) {
-            case "java_1_1": return JAVA_1_1;
-            case "java_1_2": return JAVA_1_2;
-            case "java_1_5": return JAVA_1_5;
-            case "java_1_5_dm": return JAVA_1_5_DM;
-            case "java_1_7": return JAVA_1_7;
-            case "java_1_9": return JAVA_1_9;
-            case "python_3": return PYTHON_3;
-            case "c_cpp": return C_CPP;
-            case "c_sharp": return C_SHARP;
-            case "char": return CHAR;
-            case "text": return TEXT;
-            case "scheme": return SCHEME;
-            default: return JAVA_1_9;
-        }
+    public String getDisplayName() {
+        return this.displayName;
+    }
+
+    public static LanguageOption fromDisplayName(String displayName) {
+        return Arrays.stream(LanguageOption.values())
+                .filter(languageOption -> languageOption.displayName.equals(displayName))
+                .findFirst()
+                .orElse(getDefault());
+    }
+
+    public static String[] getAllDisplayNames() {
+        return Arrays.stream(LanguageOption.values())
+                .map(languageOption -> languageOption.displayName)
+                .toArray(String[]::new);
+    }
+
+    public static LanguageOption getDefault() {
+        return LanguageOption.JAVA_1_9;
     }
 }

+ 2 - 0
jplag/src/main/java/jplag/strategy/NormalComparisonStrategy.java

@@ -39,6 +39,8 @@ public class NormalComparisonStrategy extends AbstractComparisonStrategy {
                     continue;
                 }
                 comparison = greedyStringTiling.compare(first, second);
+
+                // TODO SH: Why does this differ from the results shown in the result web page?
                 System.out.println("Comparing " + first.name + "-" + second.name + ": " + comparison.percent());
                 if (baseCodeSubmission != null) {
                     comparison.bcMatchesA = baseCodeMatches.get(comparison.firstSubmission.name);

+ 19 - 0
jplag/src/test/java/jplag/NormalComparisonTests.java

@@ -0,0 +1,19 @@
+package jplag;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class NormalComparisonTests extends TestBase {
+
+    @Test
+    public void testSimpleDuplicate() throws ExitException {
+        JPlagResult result = runJPlagWithDefaultOptions("SimpleDuplicate");
+
+        assertEquals(2, result.getNumberOfSubmissions());
+        assertEquals(1, result.getComparisons().size());
+        assertEquals(1, result.getComparisons().get(0).matches.size());
+        assertEquals(1, result.getSimilarityDistribution()[6]);
+    }
+
+}

+ 17 - 0
jplag/src/test/java/jplag/TestBase.java

@@ -0,0 +1,17 @@
+package jplag;
+
+import jplag.options.JPlagOptions;
+import jplag.options.LanguageOption;
+
+public abstract class TestBase {
+
+    protected JPlagResult runJPlagWithDefaultOptions(String testSampleName) throws ExitException {
+        JPlagOptions options = new JPlagOptions(
+                String.format("src/test/resources/samples/%s", testSampleName),
+                LanguageOption.JAVA_1_9);
+
+        JPlag jplag = new JPlag(options);
+        return jplag.run();
+    }
+
+}

+ 11 - 0
jplag/src/test/resources/samples/SimpleDuplicate/A/SimpleDuplicate.java

@@ -0,0 +1,11 @@
+public class SimpleDuplicate {
+
+    public static void main(String[] args) {
+        System.out.println("Hello World!");
+
+        for(int i = 0; i < 10; i++) {
+            System.out.println("Number is " + i);
+        }
+    }
+
+}

+ 12 - 0
jplag/src/test/resources/samples/SimpleDuplicate/B/SimpleDuplicate.java

@@ -0,0 +1,12 @@
+public class SimpleDuplicate {
+
+    public static void main(String[] args) {
+        System.out.println("Hello Plagiarism!");
+
+        int max = 10;
+        for(int j = 0; j < max; j++) {
+            System.out.println("Number is " + j);
+        }
+    }
+
+}