From 47dbc9084825d339f7446594ecf854878d410894 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Sat, 21 Apr 2018 10:09:01 -0700 Subject: [PATCH] ... --- checker/codecheck | 77 +++------ checker/codecheck.policy | 6 +- checker/runprog | 2 +- checker/src/com/horstmann/codecheck/Main.java | 153 ++++++++---------- checker/src/com/horstmann/codecheck/Util.java | 1 + .../test1/solution/{Test.java => Prog.java} | 2 +- .../test1/student/{Test.java => Prog.java} | 2 +- todo.txt | 73 +++++++++ 8 files changed, 163 insertions(+), 153 deletions(-) rename samples/java/test1/solution/{Test.java => Prog.java} (92%) rename samples/java/test1/student/{Test.java => Prog.java} (95%) diff --git a/checker/codecheck b/checker/codecheck index 46a5ce4..fcc3c88 100755 --- a/checker/codecheck +++ b/checker/codecheck @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2006-2017 Cay S. Horstmann +# Copyright 2006-2018 Cay S. Horstmann # # This file is part of CodeCheck. # @@ -40,20 +40,6 @@ function usage { exit 1 } -function copyToSubmission { - if [ -e $PROBLEMDIR/$1 ] ; then - cd $PROBLEMDIR/$1 - for f in `find .` ; do - if [ "$f" != "." ] ; then - mkdir -p $SUBMISSIONDIR/`dirname $f` - cp $f $SUBMISSIONDIR/$f - # cp --parents $f $SUBMISSIONDIR - fi - done - cd ../.. - fi -} - function grade { if [ "${SUBMISSIONDIR/#\//}" == "$SUBMISSIONDIR" ] ; then SUBMISSIONDIR="$STARTDIR"/"$SUBMISSIONDIR" ; fi @@ -81,10 +67,10 @@ function grade { if [ ! -z "$DEBUG" ] ; then echo WORKDIR=$WORKDIR - echo LC_ALL=en_US.UTF-8 $EXEC_CONTROL "$JAVA_HOME"/bin/java $JOPTS -classpath $CPATH com.horstmann.codecheck.Main $LEVEL "$SDIR" "$PDIR" $@ + echo LC_ALL=en_US.UTF-8 $EXEC_CONTROL "$JAVA_HOME"/bin/java $JOPTS -classpath $CPATH com.horstmann.codecheck.Main "$SDIR" "$PDIR" $@ fi - LC_ALL=en_US.UTF-8 $EXEC_CONTROL "$JAVA_HOME"/bin/java $JOPTS -classpath $CPATH com.horstmann.codecheck.Main $LEVEL "$SDIR" "$PDIR" $@ + LC_ALL=en_US.UTF-8 $EXEC_CONTROL "$JAVA_HOME"/bin/java $JOPTS -classpath $CPATH com.horstmann.codecheck.Main "$SDIR" "$PDIR" $@ cd "$SUBMISSIONDIR" @@ -165,8 +151,6 @@ if [ "$1" == "-s" ] ; then fi JAVA_OPTS="$JAVA_OPTS -Dcom.horstmann.codecheck.report=$REPORT_TYPE" EXEC_CONTROL="cgexec -g cpu:codecheck --sticky nice -15" - LEVEL=$1 - shift # Program is executed in submission dir SUBMISSIONDIR=$1 WORKDIR=$1 @@ -237,36 +221,15 @@ shift $((OPTIND-1)) JAVA_OPTS="$JAVA_OPTS -Dcom.horstmann.codecheck.report=$REPORT_TYPE" -if [ -z $2 ] ; then - LEVEL=1 # Checking solution -else - LEVEL=$1 - shift -fi - -if [ -z $2 ] ; then +if [[ -z $2 ]] ; then # Check solution PROBLEMDIR=$1 shift SUBMISSIONDIR=`mktemp -d /tmp/codecheck.XXXXXXXXXX` - if [ -e $PROBLEMDIR/solution ] ; then cp -R $PROBLEMDIR/solution/* $SUBMISSIONDIR ; fi - case $LEVEL in - [1-9]) - for n in `seq 1 $LEVEL` ; do - if [ -e $PROBLEMDIR/solution$n ] ; then cp -R $PROBLEMDIR/solution$n/* $SUBMISSIONDIR ; fi - done - ;; - grade) - for n in `seq 1 9` ; do - if [ -e $PROBLEMDIR/solution$n ] ; then cp -R $PROBLEMDIR/solution$n/* $SUBMISSIONDIR ; fi - done - if [ -e $PROBLEMDIR/grader ] ; then cp -R $PROBLEMDIR/grader/* $SUBMISSIONDIR ; fi - ;; - esac - - # If SUBMISSIONDIR is empty, then copy all files from base dir - if [ ! "$(ls -A $SUBMISSIONDIR)" ] ; then + if [ -e $PROBLEMDIR/solution ] ; then + cp -R $PROBLEMDIR/solution/* $SUBMISSIONDIR + else cp $PROBLEMDIR/* $SUBMISSIONDIR fi @@ -279,9 +242,8 @@ else shift fi -if [ -z "$LEVEL" ] || [ -z "$PROBLEMDIR" ] || [ ! -d "$SUBMISSIONDIR" ] || [ ! -d "$PROBLEMDIR" ] +if [ -z "$PROBLEMDIR" ] || [ ! -d "$SUBMISSIONDIR" ] || [ ! -d "$PROBLEMDIR" ] then - if [ -z "$LEVEL" ] ; then echo "No level supplied" ; fi if [ -z "$PROBLEMDIR" ] ; then echo "No problemdir supplied" ; fi if [ ! -d "$SUBMISSIONDIR" ] ; then echo "submissiondir $SUBMISSIONDIR doesn't exist" ; fi if [ ! -d "$PROBLEMDIR" ] ; then echo "problemdir $PROBLEMDIR doesn't exist" ; fi @@ -300,20 +262,17 @@ if [ -z "$CHECK_STUDENT_DIR" ] ; then exit ; fi cd "$STARTDIR" SUBMISSIONDIR=`mktemp -d /tmp/codecheck.XXXXXXXXXX` -copyToSubmission student -case $LEVEL in - [1-9]) - for n in `seq 1 $LEVEL` ; do - copyToSubmission student$n +if [ -e $PROBLEMDIR/student ] ; then + cd $PROBLEMDIR/student + for f in `find .` ; do + if [ "$f" != "." ] ; then + mkdir -p $SUBMISSIONDIR/`dirname $f` + cp $f $SUBMISSIONDIR/$f + # cp --parents $f $SUBMISSIONDIR + fi done - ;; - grade) - for n in `seq 1 9` ; do - copyToSubmission student$n - done - copyToSubmission grader - ;; -esac + cd ../.. +fi WORKDIR=`mktemp -d /tmp/codecheck.XXXXXXXXXX` diff --git a/checker/codecheck.policy b/checker/codecheck.policy index 4a9efc2..3630abf 100644 --- a/checker/codecheck.policy +++ b/checker/codecheck.policy @@ -10,9 +10,9 @@ grant codeBase "file:${java.home}/../lib/tools.jar" { permission java.security.AllPermission; }; -grant codeBase "jrt:/jdk.compiler" { - permission java.security.AllPermission; -}; +//grant codeBase "jrt:/jdk.compiler" { +// permission java.security.AllPermission; +//}; grant { permission java.io.FilePermission "-", "read,write,delete"; diff --git a/checker/runprog b/checker/runprog index 5825392..ffd1a65 100755 --- a/checker/runprog +++ b/checker/runprog @@ -70,7 +70,7 @@ case _"$LANG" in ;; _Racket) - ulimit -d 10000 -f 1000 -n 100 -v 1000000 -t ${TIMEOUT} + ulimit -d 100000 -f 1000 -n 100 -v 1000000 -t ${TIMEOUT} if grep -qE '\(define\s+\(\s*main\s+' $1 ; then timeout ${TIMEOUT}s nice racket -tm $@ else diff --git a/checker/src/com/horstmann/codecheck/Main.java b/checker/src/com/horstmann/codecheck/Main.java index 1ad5c27..5b50795 100644 --- a/checker/src/com/horstmann/codecheck/Main.java +++ b/checker/src/com/horstmann/codecheck/Main.java @@ -1,7 +1,6 @@ package com.horstmann.codecheck; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileSystems; @@ -45,9 +44,9 @@ public class Main { private Properties checkProperties = new Properties(); private Report report; private Path problemDir; - private List studentDirectories = new ArrayList<>(); - private Set studentFiles; - private Set solutionFiles; + private Path studentDirectory; + private Set studentFiles = new TreeSet<>(); + private Set solutionFiles = new TreeSet<>(); private Set printFiles = new TreeSet<>(); private Set requiredFiles = new TreeSet<>(); // tails only private Set mainModules = new TreeSet<>(); // tails only @@ -74,9 +73,8 @@ public class Main { * Entry point to program. * * @param args - * command-line arguments. args[0] = level (1, 2, 3, ..., or - * check/grade for compatibility) args[1] = submission dir, - * args[2] = problem dir args[3] etc = metadata key=value pairs (optional) + * command-line arguments. args[0] = submission dir, + * args[1] = problem dir args[2] etc = metadata key=value pairs (optional) * @throws IOException * @throws ReflectiveOperationException */ @@ -119,15 +117,12 @@ public static Set filterNot(Set paths, String... glob) { } public void copySuppliedFiles() throws IOException { - for (String s : studentDirectories) { - Path dir = problemDir.resolve(s); - for (Path p : Util.getDescendantFiles(dir)) { - Path source = dir.resolve(p); - Path target = workDir.resolve(p); - // Copy if it is not required to be submitted - if (!requiredFiles.contains(p)) { - Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); - } + for (Path p : Util.getDescendantFiles(studentDirectory)) { + Path source = studentDirectory.resolve(p); + Path target = workDir.resolve(p); + // Copy if it is not required to be submitted + if (!requiredFiles.contains(p)) { + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); } } } @@ -235,14 +230,22 @@ private void runTester(Path mainmodule, int timeout, int maxOutputLen) throws Ex } private void testInputs(Map inputs, Path mainmodule, Annotations annotations) throws Exception { + Path runInput = workDir.resolve("Input"); + boolean inputMode = Files.exists(runInput); + + if (inputMode) { + inputs.put("Input", Util.read(runInput)); + } + /* * If there are no inputs, we feed in one empty input to execute the program. */ if (inputs.size() == 0) inputs.put("", ""); - report.header("run", "Testing " + mainmodule); + report.header("run", inputMode ? "Output" : "Testing " + mainmodule); Path solutionDir = null; - if (!annotations.isSample(mainmodule)) { + + if (!inputMode && !annotations.isSample(mainmodule)) { solutionDir = compileSolution(mainmodule, null, 0); if (solutionDir == null) return; } @@ -268,9 +271,8 @@ private void testInput(Path mainmodule, Annotations annotations, String out = annotations.findUniqueKey("OUT"); String[] outFiles = out == null ? new String[0] : out.trim().split("\\s+"); - String runNumber = test.replace("test", "").trim(); - report.run(runNumber.length() > 0 ? "Test " + runNumber : null); + report.run(!test.equals("Input") && runNumber.length() > 0 ? "Test " + runNumber : null); for (String args : runargs) { testInput(mainmodule, solutionDir, test, input, args, outFiles, timeout / runargs.size(), maxOutput / runargs.size()); @@ -289,7 +291,7 @@ private void testInput(Path mainmodule, report.args(runargs); - if (!language.echoesStdin()) report.input(input); + if (!language.echoesStdin() && !test.equals("Input")) report.input(input); // Run student program and capture stdout/err and output files @@ -373,7 +375,7 @@ else if (!mainModules.contains(tailPath) // Just in case that there is a student private void getRequiredModules() { for (Path p : solutionFiles) - if (language.isSource(p)) + if (language.isSource(p) || p.getFileName().toString().equals("Input")) requiredFiles.add(Util.tail(p)); } @@ -478,10 +480,8 @@ private void doCalls(Path submissionDir, Calls calls) throws Exception { public void run(String[] args) throws IOException, ReflectiveOperationException { // TODO: Adjustable Timeouts long startTime = System.currentTimeMillis(); - String mode = args[0].trim(); - Path submissionDir = FileSystems.getDefault().getPath(args[1]); - problemDir = FileSystems.getDefault().getPath(args[2]); - + Path submissionDir = FileSystems.getDefault().getPath(args[0]); + problemDir = FileSystems.getDefault().getPath(args[1]); Path homeDir = Util.getHomeDir(); workDir = new File(".").getAbsoluteFile().toPath().normalize(); System.setProperty("java.security.policy", homeDir.resolve("codecheck.policy").toString()); @@ -505,61 +505,35 @@ else if (System.getProperty("com.horstmann.codecheck.codioreport") != null) report = new CodioReport("Report", submissionDir); else report = new HTMLReport("Report", submissionDir); - int level = 0; - try { - level = Integer.parseInt(mode); - } catch (NumberFormatException ex) { - } - - if (mode.equals("check")) // TODO: Legacy - level = 1; - else if (mode.equals("grade")) // TODO: Legacy - level = 9; // to pick up all - if (Files.exists(problemDir.resolve("student"))) - studentDirectories.add("student"); - for (int n = 1; n <= level; n++) - if (Files.exists(problemDir.resolve("student" + n))) - studentDirectories.add("student" + n); - - // TODO: Legacy - if (mode.equals("grade")) { // mode grade will be rejected by web app - if (Files.exists(problemDir.resolve("grader"))) { - studentDirectories.add("grader"); - } - } - - List solutionDirectories = new ArrayList<>(); - if (Files.exists(problemDir.resolve("solution"))) - solutionDirectories.add("solution"); - for (int n = 1; n <= level; n++) - if (Files.exists(problemDir.resolve("solution" + n))) - solutionDirectories.add("solution" + n); - if (studentDirectories.size() + solutionDirectories.size() == 0) { - // new-style packaging with no student or solution directories - studentDirectories.add("."); + studentDirectory = problemDir.resolve("student"); + Path solutionDirectory = null; + if (Files.exists(studentDirectory)) { // old style with student and solution directories + solutionDirectory = problemDir.resolve("solution"); + } else { + studentDirectory = problemDir; } - - + String problemId = null; Annotations annotations = null; try { - // Read check.properties in level dirs - for (String levelDir : studentDirectories) { - File modeCheckProperties = problemDir.resolve(levelDir).resolve("check.properties").toFile(); - if (modeCheckProperties.exists()) { - try (InputStream in = new FileInputStream(modeCheckProperties)) { - checkProperties.load(in); - } + // Read check.properties + Path modeCheckProperties = studentDirectory.resolve("check.properties"); + if (Files.exists(modeCheckProperties)) { + try (InputStream in = Files.newInputStream(modeCheckProperties)) { + checkProperties.load(in); } } - - studentFiles = filterNot(Util.getDescendantFiles(problemDir, studentDirectories), "check.properties", ".*", "problem.ch", "problem.html"); - solutionFiles = filterNot(Util.getDescendantFiles(problemDir, solutionDirectories), "*.txt", ".*", "*.class"); - // TODO: Filtering out rubric - // TODO: Unify with server/src/Problem.java + for (Path p : filterNot(Util.getDescendantFiles(studentDirectory), + "check.properties", ".*", "index.html", "index.ch", "problem.html", "q.properties")) { + studentFiles.add(problemDir.relativize(studentDirectory.resolve(p))); + } + if (solutionDirectory != null) + for (Path p : filterNot(Util.getDescendantFiles(solutionDirectory), + "*.txt", ".*", "*.class")) + solutionFiles.add(problemDir.relativize(solutionDirectory.resolve(p))); // Determine language String languageName = System.getProperty("com.horstmann.codecheck.language"); @@ -590,20 +564,21 @@ else if (mode.equals("grade")) // TODO: Legacy Set annotatedSolutions = annotations.findSolutions(); studentFiles.removeAll(annotatedSolutions); solutionFiles.addAll(annotatedSolutions); + Path runInput = Paths.get("Input"); if (solutionFiles.size() == 0) { - if (studentFiles.size() == 1) { - // Assume that's the solution + if (studentFiles.size() == 1 || studentFiles.contains(runInput)) { + // Assume that's the solution if only one file solutionFiles.addAll(studentFiles); studentFiles.clear(); } else { - String[] delimiters = language.pseudoCommentDelimiters(); - throw new CodeCheckException("Mark one or more files as " + delimiters[0] + "SOLUTION" + delimiters[1]); + String[] delimiters = language.pseudoCommentDelimiters(); + throw new CodeCheckException( + "Mark one or more files as " + delimiters[0] + "SOLUTION" + delimiters[1]); } } report.comment("Submission", submissionDir.toString()); // This is just a unique ID, can be used to check against cheating - report.comment("Level", "" + level); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); df.setTimeZone(TimeZone.getTimeZone("UTC")); String currentTime = df.format(new Date()); @@ -620,7 +595,7 @@ else if (mode.equals("grade")) // TODO: Legacy report.comment("ID", problemId); // Used to pass in machine instance, git url into report - for (int iarg = 3; iarg < args.length; iarg++) { + for (int iarg = 2; iarg < args.length; iarg++) { String arg = args[iarg]; int keyEnd = arg.indexOf("="); if (keyEnd >= 0) { @@ -655,11 +630,9 @@ else if (mode.equals("grade")) // TODO: Legacy annotations.check(report, requiredFiles); - for (String dir : studentDirectories) { - for (Path p : Util.getDescendantFiles(problemDir.resolve(dir))) { - if (!requiredFiles.contains(p)) - printFiles.add(p); - } + for (Path p : Util.getDescendantFiles(studentDirectory)) { + if (!requiredFiles.contains(p)) + printFiles.add(p); } copySuppliedFiles(); @@ -676,7 +649,7 @@ else if (mode.equals("grade")) // TODO: Legacy } if (annotations.checkConditions(workDir, report)) { - if (getStringProperty("test.method") != null) // Legacy + if (getStringProperty("test.method") != null) // TODO: Legacy callMethod(tolerance, ignoreCase, ignoreSpace); if (annotations.has("CALL")) doCalls(submissionDir, annotations.findCalls()); @@ -686,7 +659,7 @@ else if (mode.equals("grade")) // TODO: Legacy mainModules.remove(Util.tail(sub.getFile())); } - Map inputs = new TreeMap<>(); + Map inputs = new TreeMap<>(); // TODO: Legacy for (String i : new String[] { "", "1", "2", "3", "4", "5", "6", "7", "8", "9" }) { String key = "test" + i + ".in"; String in = getStringProperty(key); @@ -747,8 +720,10 @@ else if (mode.equals("grade")) // TODO: Legacy for (Path file : requiredFiles) report.file(submissionDir, file); - printFiles = filterNot(printFiles, "test*.in", "test*.out", "check.properties", "q.properties", "*.png", "*.PNG", - "*.gif", "*.GIF", "*.jpg", "*.jpeg", "*.JPG", ".DS_Store", "*.jar", "*.class", "problem.ch", "problem.html"); + printFiles = filterNot(printFiles, "test*.in", "test*.out", "Input", + "check.properties", "q.properties", + "*.png", "*.PNG", "*.gif", "*.GIF", "*.jpg", "*.jpeg", "*.JPG", + ".DS_Store", "*.jar", "*.class", "index.html", "index.ch", "problem.html"); printFiles.removeAll(annotations.findHidden()); @@ -761,7 +736,9 @@ else if (mode.equals("grade")) // TODO: Legacy } catch (Throwable t) { report.systemError(t); } finally { - if (annotations != null && !annotations.has("NOSCORE")) report.add(score); + if (annotations != null && !annotations.has("NOSCORE") + && !Files.exists(workDir.resolve("Input"))) + report.add(score); long endTime = System.currentTimeMillis(); report.comment("Elapsed", (endTime - startTime) + " ms"); report.save(problemId, "report"); diff --git a/checker/src/com/horstmann/codecheck/Util.java b/checker/src/com/horstmann/codecheck/Util.java index 8606230..e77bb18 100644 --- a/checker/src/com/horstmann/codecheck/Util.java +++ b/checker/src/com/horstmann/codecheck/Util.java @@ -30,6 +30,7 @@ public static boolean sameContents(Path p1, Path p2) throws IOException { } public static Path tail(Path p) { + if (p.getNameCount() < 2) return p; return p.subpath(1, p.getNameCount()); } diff --git a/samples/java/test1/solution/Test.java b/samples/java/test1/solution/Prog.java similarity index 92% rename from samples/java/test1/solution/Test.java rename to samples/java/test1/solution/Prog.java index d33e7bb..ca918fa 100644 --- a/samples/java/test1/solution/Test.java +++ b/samples/java/test1/solution/Prog.java @@ -1,4 +1,4 @@ -public class Test +public class Prog { public static void main(String[] args) { diff --git a/samples/java/test1/student/Test.java b/samples/java/test1/student/Prog.java similarity index 95% rename from samples/java/test1/student/Test.java rename to samples/java/test1/student/Prog.java index 588863b..e702b5a 100644 --- a/samples/java/test1/student/Test.java +++ b/samples/java/test1/student/Prog.java @@ -3,7 +3,7 @@ For example, if name is "Udacity", set last to "y". */ -public class Test +public class Prog { public static void main(String[] args) { diff --git a/todo.txt b/todo.txt index a80ad43..f0753ee 100644 --- a/todo.txt +++ b/todo.txt @@ -1,15 +1,88 @@ =CodeCheck +sudo apt install empty-expect + +rm empty.log +empty -f -i in.fifo -o out.fifo -p empty.pid -L empty.log java Volume +echo 3 > in.fifo +echo 2 > in.fifo +cat empty.log + +<<>>3 +<<>>2 +<< in.fifo +cat empty.log + +<<>>3 +<<(C)V class com/horstmann/codecheck/JavaLanguage uses deprecated method java/lang/Thread::stop()V +--- + +(C) Can I use Java 9 from http://zulu.org/download/ or http://download.java.net/java/GA/jdk9/9/binaries/openjdk-9_linux-x64_bin.tar.gz in play-codecheck? (It's now 9.0.1) +(C) CodeCheck http://code.makery.ch/blog/javafx-2-snapshot-as-png-image/ +(C) CodeCheck: Allow students to save their work and give them a URL +(C) Codecheck eliminate levels +(C) In play project, activate some of the security filters +(C) https://www.slideshare.net/EricSmalling1/best-practices-for-developing-deploying-java-applications-with-docker, https://blog.sebastian-daschner.com/ +(C) sudo /usr/sbin/cgconfigparser -l /etc/cgconfig.conf before running CodeCheck locally + + +--- + +Kathleen: I do not use levels. I use your //SOLUTION feature and run two separate files. + +I do not use rubrics. + + +--- + +Another "to be fixed eventually" problem + +When using the picture class, if the image is not found and exception is thrown but then the student gets a pass and full marks. + +Here is the report. + +http://www.codecheck.it/fetch/17072919018705920314428255902/report.html + +This happened because I forgot to include the image, but the same thing would happen if the student misspelled the file name +--- Can we compare numbers by significant digits rather than by epsilon? +--- https://docs.racket-lang.org/rackunit/quick-start.html