diff --git a/.gitignore b/.gitignore index 750e364..132dba7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,14 @@ src/jacoco/*.jar src/defaultJars/*.jar src/JavaTddPluginSupport.jar src/junit-4.8.2.jar +src/findbugs/README.txt +src/findbugs/bin/ +src/findbugs/doc/ +src/findbugs/lib/ +src/findbugs/optionalPlugin/ +src/findbugs/plugin/ +src/findbugs/src/ +src/pmd/*.jar +src/pmd/LICENSE +src/checkstyle/*.jar +src/pit/*.jar diff --git a/src/JavaTddPluginSupport.jar b/src/JavaTddPluginSupport.jar index ba51455..9bac3fc 100644 Binary files a/src/JavaTddPluginSupport.jar and b/src/JavaTddPluginSupport.jar differ diff --git a/src/build.xml b/src/build.xml index f7b535b..a81487a 100644 --- a/src/build.xml +++ b/src/build.xml @@ -6,6 +6,7 @@ + + @@ -142,6 +148,20 @@ + + + + + + + + + + + + + + @@ -264,6 +286,7 @@ staticAnalysisSrcExclusionPattern = ${staticAnalysisSrcExclusionPattern} timeout="${exec.timeout}"> + @@ -338,6 +361,7 @@ staticAnalysisSrcExclusionPattern = ${staticAnalysisSrcExclusionPattern} timeout="${exec.timeout}"> + @@ -405,6 +429,7 @@ staticAnalysisSrcExclusionPattern = ${staticAnalysisSrcExclusionPattern} @@ -443,7 +468,7 @@ staticAnalysisSrcExclusionPattern = ${staticAnalysisSrcExclusionPattern} - + + + + + + + + + + @@ -704,7 +817,7 @@ staticAnalysisSrcExclusionPattern = ${staticAnalysisSrcExclusionPattern} The main target ============================================================ --> - + @@ -713,6 +826,7 @@ staticAnalysisSrcExclusionPattern = ${staticAnalysisSrcExclusionPattern} + @@ -727,6 +841,9 @@ staticAnalysisSrcExclusionPattern = ${staticAnalysisSrcExclusionPattern} + + + diff --git a/src/checkstyle.xml b/src/checkstyle.xml index 2158fe4..4794625 100644 --- a/src/checkstyle.xml +++ b/src/checkstyle.xml @@ -1,494 +1,495 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/checkstyle/README.md b/src/checkstyle/README.md new file mode 100644 index 0000000..4b47311 --- /dev/null +++ b/src/checkstyle/README.md @@ -0,0 +1,8 @@ +# Checkstyle + +Jars in this folder are used to provide the current version of +checkstyle used by the plugin. + +The current version used is: 8.35 + +https://github.com/checkstyle/checkstyle/releases/download/checkstyle-8.35/checkstyle-8.35-all.jar diff --git a/src/config.plist b/src/config.plist index 20d9d7e..99d6e34 100644 --- a/src/config.plist +++ b/src/config.plist @@ -1,775 +1,829 @@ -{ - name = "JavaTddPlugin"; - version.major = 4; - version.minor = 1; - version.revision = 2; - version.date = 20190420; - autoPublish = true; - requires = ( ANTForPlugins, PerlForPlugins, - PMDForPlugins, CheckstyleForPlugins ); - provider = "Virginia Tech Computer Science"; - provider.url = "http://web-cat.org/updates"; - license = "GNU Affero General Public License v.3"; - license.url = "http://www.gnu.org/licenses/agpl.html"; - copyright = - "(c) 2006-2018 Virginia Tech Department of Computer Science"; - info.url = "http://wiki.web-cat.org/WCWiki/JavaTddPlugin"; - history.url = - "http://wiki.web-cat.org/WCWiki/JavaTddPlugin/ChangeHistory"; - executable = execute.pl; - interpreter.prefix = "${PerlForPlugins.perl.exe}"; - author = "Stephen Edwards (edwards@cs.vt.edu)"; - authorUid = edwards; - languages = ( { name = Java; version = 1.4; }, - { name = Java; version = 1.5; }, - { name = Java; version = 1.6; }, - { name = Java; version = 1.7; }, - { name = Java; version = 1.8; } ); - description = "This \"all-in-one\" plug-in is designed to provide full - processing and feedback generation for Java assignments where - students write their own JUnit test cases. - It includes ANT-based compilation, JUnit processing of student-written - tests, support for instructor-written reference tests, PMD and - Checkstyle analysis, and Clover-based tracking of code coverage - during student testing."; - timeoutMultiplier = 2; - timeoutInternalPadding = 400; - assignmentOptions = ( - { - property = testCases; - type = fileOrDir; - fileTypes = ( java ); - name = "Hidden JUnit Reference Test Class(es)"; - description = - "A Java source file (or directory of source files) containing JUnit tests - to run against student code to assess completeness of problem coverage. - Test outcomes are not directly visible to students, an students only see - hints about what may be incorrect. - If you select a single Java file, it must contain a JUnit test class - declared in the default package. If you select a directory, it should - contain JUnit test classes arranged in subdirectories according to their - Java package declarations. If you make no selection, an empty set of - instructor reference tests will be used instead."; - }, -/* { - property = visibleTestCases; - type = fileOrDir; - fileTypes = ( java ); - name = "Visible JUnit Reference Test Class(es)"; - description = - "A Java source file (or directory of source files) containing JUnit tests - to run against student code to assess completeness of problem coverage. - \"Visible\" here means pass/fail information for every test is shown (not - limited by hint controls). - If you select a single Java file, it must contain a JUnit test class - declared in the default package. If you select a directory, it should - contain JUnit test classes arranged in subdirectories according to their - Java package declarations. If you make no selection, an empty set of - instructor reference tests will be used instead."; - }, -*/ { - property = assignmentJar; - type = fileOrDir; - fileTypes = ( jar ); - name = "Supplemental Classes for Assignment"; - description = - "A jar file (or a directory of class files in subdirs reflecting their - package structure, or a directory of multiple jar files) containing - precompiled classes to add to the classpath when compiling and running - submissions for this assignment. If you want to apply the same - jar settings to many assignments, use the \"Supplemental Classes\" setting - in the \"Reusable Configuration Options\" section instead. If you have - multiple jars to provide, place them all in the same directory in your - Web-CAT file space and then select the whole directory."; - }, - { - property = localFiles; - type = fileOrDir; - name = "Data Files for Student"; - description = - "A file (or a directory of files) to place in the student's current working - directory when running his/her tests and when running reference tests. The - file you select (or the entire contents of the directory you select) will be - copied into the current working directory during grading so that - student-written and instructor-written test cases can read and/or write to - the file(s). The default is to copy no files."; - } - ); - optionCategories = ( - "Basic Settings", - "Advanced Settings", - "Developer Settings" - ); - options = ( - { - property = useAssertions; - type = boolean; - default = true; - name = "Use Java Assertions"; - category = "Basic Settings"; - description = - "Enable Java assertions during execution. When set to false, assertions - in student or instructor-provided code will be treated as non-executable - (no-op's)."; - }, - { - property = useDefaultJar; - type = boolean; - default = true; - name = "Use Built-in Jars"; - category = "Basic Settings"; - description = - "Set to true to have a set of built-in jars containing Virginia Tech - CS 1/CS 2 classes placed on the classpath for assignments. Set to - false to omit these jars from the classpath."; - }, - { - property = classpathJar; - type = fileOrDir; - fileTypes = ( jar ); - name = "Predefined Classes"; - category = "Basic Settings"; - description = - "A jar file (or a directory of class files in subdirs reflecting their - package structure, or a directory of multiple jar files) containing - precompiled classes to add to the classpath when compiling and running - submissions. Use this setting if you'd like to share the same jar(s) - across several assignments. If you have multiple jars to provide, - place them all in the same directory in your Web-CAT file space and - then select the whole directory."; - }, - { - property = useXvfb; - type = boolean; - default = false; - name = "Enable Xvfb During Test Execution"; - category = "Advanced Settings"; - description = - "This option is necessary for running GUI software tests on Linux servers. - It uses an instance of Xvfb, the X virtual frame buffer server, to run - unit tests so that GUI tests can render to a live X server. Note that - enabling this option will slow down test execution, since it takes time to - start up an X server, but it is required for GUI testing on a linux - server. Also, this option requires that Xvfb is already installed on - the server."; - }, - { - property = policyFile; - advanced = true; - type = file; - fileTypes = ( policy ); - name = "Java Security Policy"; - category = "Advanced Settings"; - description = - "A Java security policy file used to limit actions on student programs at - run-time. Leave unset to use the built-in default, which plugs most - security holes and prevents any file system access outside the subtree - rooted at the program's working directory."; - }, - { - property = remote.post.url; - advanced = true; - type = shortText; - size = 40; - name = "Remotely Post Submissions"; - category = "Advanced Settings"; - description = - "A URL to which submissions will be posted as they are processed, to - allow for external tools to receive student submissions. If a URL is - specified an HTML POST request will be sent to the given URL, with the - student's user name provided in the parameter 'user', and the student's - submission file provided in the parameter 'uploadedfile'."; - }, - { - property = "grader.partnerExcludePatterns"; - advanced = true; - type = shortText; - size = 40; - name = "Partner Name Exclude Patterns"; - category = "Basic Settings"; - description = - "The plug-in will automatically scan @author tags in source code - to try to identify the user names of partners when partners are allowed - on an assignment. Here, you can provide a comma-separated list of names - to exclude from consideration (e.g., the names of instructors, if some - instructor names are listed in @author lines in pre-provided code). - Full Perl-style regular expressions can be used if desired."; - }, - { - property = allStudentTestsMustPass; - type = boolean; - default = false; - name = "All Student Tests Must Pass"; - category = "Basic Settings"; - description = - "If you are truly following test-driven development practices, then no code - is ever released until all of its unit tests pass. If this option is set to - true, students will not receive a non-zero score or receive further - assessment feedback unless all student tests pass. If this option is not - set, then students may continue to proceed even if some student-written - tests fail The student's correctness/testing score is multiplied by the - proportion of their tests that pass."; - }, - { - property = studentsMustSubmitTests; - type = boolean; - default = true; - name = "Students Must Submit Tests"; - category = "Basic Settings"; - description = - "When set, this option requires all students to submit test cases for their - own code. Submissions without test cases will received feedback to that - effect (and no more), as well as a zero score. If you unset this option, - then student submissions will not be required to include - student-written test cases, and only the reference test pass rate - will be used for scoring (i.e., student code coverage and student test pass - rate will not be included in scoring)."; - }, - { - property = coverageMetric; - advanced = true; - type = radioChoice; - name = "Test Coverage Metric"; - category = "Basic Settings"; - default = 0; - description = "Choose the criterion used to measure how thoroughly - a student's tests cover the corresponding code."; - choices = ( { label = "Methods executed"; value = 0; }, - { label = "Lines executed"; value = 1; }, - { label = "Methods + conditions executed"; - value = 2; }, - { label = "Lines + conditions executed"; - value = 3; }, - { label = "Methods + lines + conditions executed"; - value = 4; } - ); - }, - { - property = coverageGoal; - type = double; - name = "Test Coverage Goal"; - category = "Basic Settings"; - description = - "If students are required to submit tests, this value is the target test - coverage threshold that must be achieved in order for students to receive - full credit. It should be a number between 0.0-100.0 representing the - minimum percent coverage required for full credit. The default is 100.0, - but a lower value may be used to allow students some slack in test coverage - when JaCoCo test coverage measures make achieving 100% too challenging."; - }, - { - property = includeTestSuitesInCoverage; - type = boolean; - default = false; - name = "Include Student Test Code in Coverage Measures"; - category = "Basic Settings"; - description = - "Normally, this plug-in excludes student-written tests from all coverage - calculations, since the goal of the testing is to test the solution, not - to execute more tests. When this option is set, student-written tests - will be included in code coverage measures for the purposes of scoring, - meaning that students will have to execute all of the code in their - test classes."; - }, - { - property = requireSimpleExceptionCoverage; - type = boolean; - default = false; - name = "Require Coverage of Simple Catch Blocks"; - category = "Basic Settings"; - description = - "When set, this option requires students to test all catch blocks, including - simple try/catch statements that are provided purely for compiler compliance - and that simply print a stack trace or re-throw the exception. If unchecked, - catch blocks that contain only a statement to print the stack trace or - re-throw a wrapped version of the exception are not counted in - coverage measurements, and do not result in point deductions."; - }, - { - property = requireSimpleGetterSetterCoverage; - type = boolean; - default = false; - name = "Require Coverage of Simple Getters and Setters"; - category = "Basic Settings"; - description = - "When set, this option requires students to test all simple getter and - setter methods. A simple getter is a method whose name starts with - \"get\", and whose body simply returns a field value. A simple setter is - a void method whose name starts with \"set\" accepting one parameter, and - whose body simply assigns that parameter to a field. If unchecked, - simple getters and setters are not counted in coverage measurements, and - do not result in point deductions."; - }, -/* - { - property = "clover.includes"; - type = shortText; - size = 40; - default = "**"; - name = "Classes to Include in Coverage Measures"; - category = "Basic Settings"; - description = - "Specify the Java file names that should be included in Clover coverage - analysis. Only student class files with names that match the patterns you - list here will be processed by Clover. Patterns are - case-insensitive. Use * as a wildcard character (ANT-style pattern - matching is used)."; - }, - { - property = "clover.excludes"; - type = shortText; - size = 40; - default = "none"; - name = "Classes to Exclude from Coverage Measures"; - category = "Basic Settings"; - description = - "Specify Java file names that should not be processed by - Clover. Any classes that match the \"Classes to Include in Coverage - Measures\" above and also match the patterns you list here - will not be processed by Clover. Patterns are - case-insensitive. Use * as a wildcard character (ANT-style pattern - matching is used). Use \"none\" if you do not wish to use any - exclusion patterns."; - }, -*/ - { - property = studentTestInclude; - type = shortText; - size = 40; - default = "*test *tests"; - name = "Students Test Class Patterns"; - category = "Basic Settings"; - description = - "Specify the Java class names that should be treated as JUnit-style - test cases. Only student classes with names that match the patterns you - list here will be executed as test cases. Patterns are case-insensitive. - Use * as a wildcard character (ANT-style pattern matching is used)."; - }, - { - property = studentTestExclude; - type = shortText; - size = 40; - default = "abstract* *$*"; - name = "Students Test Class Exclusion Patterns"; - category = "Basic Settings"; - description = - "Specify Java class names that should not be treated as JUnit-style - test cases. Any classes that match the \"Student Test Class Patterns\" - above and also match the patterns you list here - will not be treated as executable test cases. Patterns are - case-insensitive. Use * as a wildcard character (ANT-style pattern matching - is used). Use \"none\" if you do not wish to use any exclusion patterns."; - }, - { - property = refTestInclude; - type = shortText; - size = 40; - default = "*"; - name = "Reference Test Class Patterns"; - category = "Basic Settings"; - description = - "Specify the Java class names that should be treated as JUnit-style - test cases when selected as reference tests by an instructor. This - setting is only relevant when an instructor selects an entire directory - or folder of Java classes. In that case, only instructor reference classes - with names that match the patterns you list here will be executed as test - cases. Patterns are case-insensitive. Use * as a wildcard character - (ANT-style pattern matching is used)."; - }, - { - property = refTestExclude; - type = shortText; - size = 40; - default = "abstract* *$*"; - name = "Reference Test Class Exclusion Patterns"; - category = "Basic Settings"; - description = - "Specify Java class names that should not be treated as JUnit-style - test cases when selected as reference tests by an instructor. This - setting is only relevant when an instructor selects an entire directory or - folder of Java classes. In that case, any classes that match the - \"Reference Test Class Patterns\" above and also match the - patterns you list here will not be treated as executable test cases. - Patterns are case-insensitive. Use * as a wildcard character (ANT-style - pattern matching is used)."; - }, - { - property = student.testingsupport.junit4.AdaptiveTimeout.ceiling; - advanced = true; - type = integer; - name = "Default Test Case Time Limit (in ms)"; - category = "Basic Settings"; - description = - "This plug-in provides built-in adaptive detection and termination of - infinite loops that occur within individual test methods. This setting - controls the default timeout before a single test case method is judged as - \"taking too long\", although this amount will be gradually increased up - to a higher maximum value if test methods terminate but come close to - this limit. The default if unset is ten seconds (10000 ms)."; - }, - { - property = student.testingsupport.junit4.AdaptiveTimeout.maximum; - advanced = true; - type = integer; - name = "Default Test Case Maximum Time (in ms)"; - category = "Basic Settings"; - description = - "This plug-in provides built-in adaptive detection and termination of - infinite loops that occur within individual test methods. This setting - controls the maximum allowable timeout before a single test case is judged - as \"taking too long\". The default if unset is twenty seconds (20000 ms)."; - }, - { - property = disableCheckstyle; - type = antBoolean; - name = "Turn Checkstyle Off"; - category = "Basic Settings"; - description = - "Disable Checkstyle for static analysis entirely (no need to upload a - separate configuration file to turn it off)."; - }, - { - property = checkstyleConfig; - advanced = true; - type = file; - fileTypes = ( xml ); - name = "Checkstyle Configuration"; - category = "Basic Settings"; - description = - "An XML file containing a Checkstyle rule configuration (see the - Checksyle - documentation). This plug-in uses Checkstyle v5.6. If you would - like to turn off all Checkstyle checks entirely, use the \"Turn - Checkstyle Off\" option instead."; - }, - { - property = disablePmd; - type = antBoolean; - name = "Turn PMD Off"; - category = "Basic Settings"; - description = - "Disable PMD for static analysis entirely (no need to upload a - separate configuration file to turn it off)."; - }, - { - property = pmdConfig; - advanced = true; - type = file; - fileTypes = ( xml ); - name = "PMD Configuration"; - category = "Basic Settings"; - description = - "An XML file containing a set of PMD rules (see the - PMD - documentation). This plug-in uses PMD v5.0.5. If you owuld like to - turn off all PMD checks entirely, use the \"Turn PMD Off\" options instead."; - }, - { - property = use.comtor; - type = antBoolean; - name = "Run COMTOR Comment Analyzer"; - category = "Basic Settings"; - description = - "Set to true to run the COMTOR Comment Analyzer and include the results - as part of the overall feedback report."; - }, - { - property = staticAnalysisInclude; - type = shortText; - size = 40; - default = "*"; - name = "Classes to Analyze"; - category = "Basic Settings"; - description = - "Specify the Java class names that should be included in Checkstyle and - PMD analysis. Only student classes with names that match the patterns you - list here will be processed by Checkstyle and PMD. Patterns are - case-insensitive. Use * as a wildcard character (ANT-style pattern - matching is used)."; - }, - { - property = staticAnalysisExclude; - type = shortText; - size = 40; - default = "none"; - name = "Classes to Exclude from Analysis"; - category = "Basic Settings"; - description = - "Specify Java class names that should not be processed by - Checkstyle or PMD. Any classes that match the \"Classes to Analyze\" - above and also match the patterns you list here - will not be processed by Checkstyle or PMD. Patterns are - case-insensitive. Use * as a wildcard character (ANT-style pattern - matching is used). Use \"none\" if you do not wish to use any - exclusion patterns."; - }, - { - property = markupProperties; - advanced = true; - type = file; - fileTypes = ( properties ); - name = "Static Analysis Scoring Scheme"; - category = "Basic Settings"; - description = - "A Java properties file containing the point deductions and limits to - use for messages generated by Checkstyle or PMD. The point deductions - are specified in a fairly generic way so they can be used for many - assignments. Deductions in the default scheme are typically 1, 2, or 5 - 'points', which are really simply relative weights. Specify a scaling - factor below to adjust how these weights are translated into point - deductions for a student."; - }, - { - property = toolDeductionScaleFactor; - advanced = true; - type = double; - name = "Static Analysis Deduction Scaling Factor"; - category = "Basic Settings"; - description = - "The Static Analysis Scoring Scheme above defines the point deductions - and limits to use for messages generated by Checkstyle or PMD in a generic - way, with most deductions in the default scheme being 1, 2, or 5 points. - Deductions in the static analysis scoring scheme are multiplied by this - factor to translate them into actual 'point deductions' shown to the - student."; - }, - { - property = hintsLimit; - type = integer; - default = 3; - name = "Hints Limit"; - category = "Basic Settings"; - description = - "Maximum number of hints the student will receive from failed reference - tests."; - }, - { - property = minCoverageLevel; - type = double; - name = "Minimum Test Coverage for Hints"; - category = "Basic Settings"; - description = - "If students are required to submit tests, this value is a minimum test - coverage threshold that must be achieved in order for any hints to be - given. It should be a number between 0.0-100.0 representing the minimum - percent coverage required to see hints."; - }, - { - property = junitErrorsHideHints; - type = boolean; - default = false; - name = "Clean JUnit Tests Required for Hints"; - category = "Basic Settings"; - description = - "If students are required to submit tests, this option requires all test - case classes to be free of PMD-based JUnit style errors, such as failing - to include at least one test method in each test case class, failing - to include assert*() calls in each test case method, or using \"bogus\" - assertions such as assertEquals(1, 1)."; - }, - { - property = hideHintsWithin; - advanced = true; - type = integer; - default = 0; - name = "Hide Hints X Days Before Deadline"; - category = "Basic Settings"; - description = - "Suppress all hints from failed reference tests for submissions within this - many days of the deadline (set to zero for hints to always be visible). - This setting allows the instructor to \"hide\" hints close to the assignment - deadline in an attempt to encourage students to start working earlier."; - }, - { - property = showHintsWithin; - advanced = true; - type = integer; - default = 0; - name = "Show Hints X Days Before Deadline"; - category = "Basic Settings"; - description = - "Show hints (up to the Hints Limit) from failed reference tests for - submissions within this many days of the deadline (only useful when Hide - Hints X Days Before Deadline is non-zero, to restore hints as the - deadline approaches)."; - }, - { - property = wantPDF; - type = boolean; - default = false; - name = "Generate PDF Printouts"; - category = "Basic Settings"; - description = - "Set to true if you wish for a single PDF file containing a pretty-printed - source code printout to be generated from the student's code. The printout - will be downloadable by students, and accessible by TAs during grading. - Note: This option uses both enscript and - ps2pdf as external commands, and requires these programs to - be correctly installed and configured."; - }, -/* - { - property = enscriptStyle; - type = shortText; - size = 15; - default = "msvc"; - name = "PDF Formatting Style (for Enscript)"; - category = "Basic Settings"; - description = - "If you are generating PDF printouts, you can specify the formatting style - used by enscript. This name will be passed to - enscript using its --style= parameter. See your - enscript documentation for more information about the styles that are - supported. Many enscript installations support the following styles: - a2ps, emacs, emacs-verbose, - ifh, and msvc. It is possible to add your - own custom formatting definitions to your enscript installation and then - use your own style name here as well."; - }, -*/ - { - property = wantClassDiagrams; - type = boolean; - default = true; - name = "Generate Class Diagrams"; - category = "Basic Settings"; - description = - "Set to true if you wish to generate class diagrams from students' code. - The diagrams will be viewable by students, and accessible by TAs during - grading. Note: This option uses both doxygen and - dot as external commands, and requires these programs to - be correctly installed and configured in the JavaTddPlugin's global - configuration options."; - }, - { - property = debug; - type = integer; - advanced = true; - default = 0; - name = "Debug Level"; - category = "Developer Settings"; - description = - "Set to a non-zero value for the script to produce debugging output (the - larger the number, the greater the detail, up to about 5). Debugging output - on each grading script run will be e-mailed to the instructor."; - }, - { - property = doNotDelete; - type = antBoolean; - advanced = true; - name = "Preserve Derived Files"; - category = "Developer Settings"; - description = - "Set to true to prevent the plug-in from deleting the derived files it - creates during the build/test process for each submission. Normally, these - files are deleted when a given submission has been completely processed. - This setting is provided for debugging purposes, when one wishes to - inspect the intermediate test driver source code or other derived files."; - }, - { - property = generateHeatmaps; - type = antBoolean; - advanced = true; - name = "Generate Bug Heatmaps"; - category = "Developer Settings"; - description = - "Set to true to generate GZoltar-based defect heatmaps. This option is - experimental and for research use only. Using it will slow down - generation of student feedback."; - }, - { - property = useEnhancedFeedback; - type = boolean; - advanced = true; - default = false; - name = "Use Enhanced Feedback (Experimental)"; - category = "Developer Settings"; - description = - "Set to true to use the new (experimental) enhanced feedback layout."; - }, - { - property = useIndicatorFeedback; - type = boolean; - advanced = true; - default = false; - name = "Use Growth Mindset Feedback (Experimental)"; - category = "Developer Settings"; - description = - "Set to true to use the new (experimental) growth mindset progress feedback."; - }, - { - property = useMaria; - type = boolean; - advanced = true; - default = false; - name = "Use Maria (Experimental)"; - category = "Developer Settings"; - description = - "Set to true to show Maria (experimental), the virtual teaching assistant - chatbot."; - }, - { - property = useMariaExplanations; - type = boolean; - advanced = true; - default = false; - name = "Use Maria Explanations (Experimental)"; - category = "Developer Settings"; - description = - "Set to true to show \"Explain...\" links (experimental) by error messages."; - }, - { - property = useDailyMissions; - type = boolean; - advanced = true; - default = false; - name = "Use Daily Missions (Experimental)"; - category = "Developer Settings"; - description = - "Set to true to give students daily mission challenges (experimental)."; - }, - { - property = showAllTestOutcomes; - type = boolean; - advanced = true; - default = false; - name = "Show All Test Outcomes (Experimental)"; - category = "Developer Settings"; - description = - "Set to true to use the new (experimental) feature to show all test outcomes - instead of limited hints. Students still need to meet the coverage - requirements and other requirements to see test outcomes, but will see - the full table of tests instead of limited hints."; - } - ); - globalOptions = ( - { - property = doxygenDir; - type = shortText; - size = 40; - name = "Doxygen Directory"; - description = - "The directory on the local server that contains the Doxygen executable. - Doxygen (and Dot, below) are used to generate class diagrams for the code - that students submit; if you do not have a copy of Doxygen or wish to - disable diagram generation across all assignments, you can leave this - field blank. If you are not the user administering this Web-CAT server, - you will need to have your system administrator install this tool and set - this path if you wish to use it."; - }, - { - property = dotDir; - type = shortText; - size = 40; - name = "Dot Directory"; - description = - "The directory on the local server that contains the Dot executable (from - the Graphviz package). Dot (and Doxygen, above) are used to generate - class diagrams for the code that students submit; if you do not have a copy - of Dot or wish to disable diagram generation across all assignments, you can - leave this field blank. If you are not the user administering this Web-CAT - server, you will need to have your system administrator install this tool - and set this path if you wish to use it."; - } - ); -} +{ + name = "JavaTddPlugin"; + version.major = 4; + version.minor = 1; + version.revision = 3; + version.date = 20220206; + autoPublish = true; + requires = ( ANTForPlugins, PerlForPlugins, + PMDForPlugins, CheckstyleForPlugins ); + provider = "Virginia Tech Computer Science"; + provider.url = "http://web-cat.org/updates"; + license = "GNU Affero General Public License v.3"; + license.url = "http://www.gnu.org/licenses/agpl.html"; + copyright = + "(c) 2006-2022 Virginia Tech Department of Computer Science"; + info.url = "http://wiki.web-cat.org/WCWiki/JavaTddPlugin"; + history.url = + "http://wiki.web-cat.org/WCWiki/JavaTddPlugin/ChangeHistory"; + executable = execute.pl; + interpreter.prefix = "${PerlForPlugins.perl.exe}"; + author = "Stephen Edwards (edwards@cs.vt.edu)"; + authorUid = edwards; + languages = ( { name = Java; version = 1.14; } ); + description = "This \"all-in-one\" plug-in is designed to provide full + processing and feedback generation for Java assignments where + students write their own JUnit test cases. + It includes ANT-based compilation, JUnit processing of student-written + tests, support for instructor-written reference tests, PMD and + Checkstyle analysis, and Clover-based tracking of code coverage + during student testing."; + timeoutMultiplier = 2; + timeoutInternalPadding = 400; + assignmentOptions = ( + { + property = testCases; + type = fileOrDir; + fileTypes = ( java ); + name = "Hidden JUnit Reference Test Class(es)"; + description = + "A Java source file (or directory of source files) containing JUnit tests + to run against student code to assess completeness of problem coverage. + Test outcomes are not directly visible to students, an students only see + hints about what may be incorrect. + If you select a single Java file, it must contain a JUnit test class + declared in the default package. If you select a directory, it should + contain JUnit test classes arranged in subdirectories according to their + Java package declarations. If you make no selection, an empty set of + instructor reference tests will be used instead."; + }, +/* { + property = visibleTestCases; + type = fileOrDir; + fileTypes = ( java ); + name = "Visible JUnit Reference Test Class(es)"; + description = + "A Java source file (or directory of source files) containing JUnit tests + to run against student code to assess completeness of problem coverage. + \"Visible\" here means pass/fail information for every test is shown (not + limited by hint controls). + If you select a single Java file, it must contain a JUnit test class + declared in the default package. If you select a directory, it should + contain JUnit test classes arranged in subdirectories according to their + Java package declarations. If you make no selection, an empty set of + instructor reference tests will be used instead."; + }, +*/ { + property = assignmentJar; + type = fileOrDir; + fileTypes = ( jar ); + name = "Supplemental Classes for Assignment"; + description = + "A jar file (or a directory of class files in subdirs reflecting their + package structure, or a directory of multiple jar files) containing + precompiled classes to add to the classpath when compiling and running + submissions for this assignment. If you want to apply the same + jar settings to many assignments, use the \"Supplemental Classes\" setting + in the \"Reusable Configuration Options\" section instead. If you have + multiple jars to provide, place them all in the same directory in your + Web-CAT file space and then select the whole directory."; + }, + { + property = localFiles; + type = fileOrDir; + name = "Data Files for Student"; + description = + "A file (or a directory of files) to place in the student's current working + directory when running his/her tests and when running reference tests. The + file you select (or the entire contents of the directory you select) will be + copied into the current working directory during grading so that + student-written and instructor-written test cases can read and/or write to + the file(s). The default is to copy no files."; + } + ); + optionCategories = ( + "Basic Settings", + "Advanced Settings", + "Developer Settings" + ); + options = ( + { + property = useAssertions; + type = boolean; + default = true; + name = "Use Java Assertions"; + category = "Basic Settings"; + description = + "Enable Java assertions during execution. When set to false, assertions + in student or instructor-provided code will be treated as non-executable + (no-op's)."; + }, + { + property = useDefaultJar; + type = boolean; + default = true; + name = "Use Built-in Jars"; + category = "Basic Settings"; + description = + "Set to true to have a set of built-in jars containing Virginia Tech + CS 1/CS 2 classes placed on the classpath for assignments. Set to + false to omit these jars from the classpath."; + }, + { + property = classpathJar; + type = fileOrDir; + fileTypes = ( jar ); + name = "Predefined Classes"; + category = "Basic Settings"; + description = + "A jar file (or a directory of class files in subdirs reflecting their + package structure, or a directory of multiple jar files) containing + precompiled classes to add to the classpath when compiling and running + submissions. Use this setting if you'd like to share the same jar(s) + across several assignments. If you have multiple jars to provide, + place them all in the same directory in your Web-CAT file space and + then select the whole directory."; + }, + { + property = useXvfb; + type = boolean; + default = false; + name = "Enable Xvfb During Test Execution"; + category = "Advanced Settings"; + description = + "This option is necessary for running GUI software tests on Linux servers. + It uses an instance of Xvfb, the X virtual frame buffer server, to run + unit tests so that GUI tests can render to a live X server. Note that + enabling this option will slow down test execution, since it takes time to + start up an X server, but it is required for GUI testing on a linux + server. Also, this option requires that Xvfb is already installed on + the server."; + }, + { + property = policyFile; + advanced = true; + type = file; + fileTypes = ( policy ); + name = "Java Security Policy"; + category = "Advanced Settings"; + description = + "A Java security policy file used to limit actions on student programs at + run-time. Leave unset to use the built-in default, which plugs most + security holes and prevents any file system access outside the subtree + rooted at the program's working directory."; + }, + { + property = remote.post.url; + advanced = true; + type = shortText; + size = 40; + name = "Remotely Post Submissions"; + category = "Advanced Settings"; + description = + "A URL to which submissions will be posted as they are processed, to + allow for external tools to receive student submissions. If a URL is + specified an HTML POST request will be sent to the given URL, with the + student's user name provided in the parameter 'user', and the student's + submission file provided in the parameter 'uploadedfile'."; + }, + { + property = "grader.partnerExcludePatterns"; + advanced = true; + type = shortText; + size = 40; + name = "Partner Name Exclude Patterns"; + category = "Basic Settings"; + description = + "The plug-in will automatically scan @author tags in source code + to try to identify the user names of partners when partners are allowed + on an assignment. Here, you can provide a comma-separated list of names + to exclude from consideration (e.g., the names of instructors, if some + instructor names are listed in @author lines in pre-provided code). + Full Perl-style regular expressions can be used if desired."; + }, + { + property = allStudentTestsMustPass; + type = boolean; + default = false; + name = "All Student Tests Must Pass"; + category = "Basic Settings"; + description = + "If you are truly following test-driven development practices, then no code + is ever released until all of its unit tests pass. If this option is set to + true, students will not receive a non-zero score or receive further + assessment feedback unless all student tests pass. If this option is not + set, then students may continue to proceed even if some student-written + tests fail The student's correctness/testing score is multiplied by the + proportion of their tests that pass."; + }, + { + property = studentsMustSubmitTests; + type = boolean; + default = true; + name = "Students Must Submit Tests"; + category = "Basic Settings"; + description = + "When set, this option requires all students to submit test cases for their + own code. Submissions without test cases will received feedback to that + effect (and no more), as well as a zero score. If you unset this option, + then student submissions will not be required to include + student-written test cases, and only the reference test pass rate + will be used for scoring (i.e., student code coverage and student test pass + rate will not be included in scoring)."; + }, + { + property = includeStudentTestsInGrading; + type = boolean; + default = true; + name = "Include Student Test Results in Grading"; + category = "Basic Settings"; + description = + "When set, if students are required to submit tests, they are also included + in the scoring formula. When false, student test pass/fail ressults are + not included in the score calculation."; + }, + { + property = coverageMetric; + advanced = true; + type = radioChoice; + name = "Test Coverage Metric"; + category = "Basic Settings"; + default = 0; + description = "Choose the criterion used to measure how thoroughly + a student's tests cover the corresponding code."; + choices = ( { label = "Methods executed"; value = 0; }, + { label = "Lines executed"; value = 1; }, + { label = "Methods + conditions executed"; + value = 2; }, + { label = "Lines + conditions executed"; + value = 3; }, + { label = "Methods + lines + conditions executed"; + value = 4; } + ); + }, + { + property = coverageGoal; + type = double; + name = "Test Coverage Goal"; + category = "Basic Settings"; + description = + "If students are required to submit tests, this value is the target test + coverage threshold that must be achieved in order for students to receive + full credit. It should be a number between 0.0-100.0 representing the + minimum percent coverage required for full credit. The default is 100.0, + but a lower value may be used to allow students some slack in test coverage + when JaCoCo test coverage measures make achieving 100% too challenging."; + }, + { + property = includeTestSuitesInCoverage; + type = boolean; + default = false; + name = "Include Student Test Code in Coverage Measures"; + category = "Basic Settings"; + description = + "Normally, this plug-in excludes student-written tests from all coverage + calculations, since the goal of the testing is to test the solution, not + to execute more tests. When this option is set, student-written tests + will be included in code coverage measures for the purposes of scoring, + meaning that students will have to execute all of the code in their + test classes."; + }, + { + property = requireSimpleExceptionCoverage; + type = boolean; + default = false; + name = "Require Coverage of Simple Catch Blocks"; + category = "Basic Settings"; + description = + "When set, this option requires students to test all catch blocks, including + simple try/catch statements that are provided purely for compiler compliance + and that simply print a stack trace or re-throw the exception. If unchecked, + catch blocks that contain only a statement to print the stack trace or + re-throw a wrapped version of the exception are not counted in + coverage measurements, and do not result in point deductions."; + }, + { + property = requireSimpleGetterSetterCoverage; + type = boolean; + default = false; + name = "Require Coverage of Simple Getters and Setters"; + category = "Basic Settings"; + description = + "When set, this option requires students to test all simple getter and + setter methods. A simple getter is a method whose name starts with + \"get\", and whose body simply returns a field value. A simple setter is + a void method whose name starts with \"set\" accepting one parameter, and + whose body simply assigns that parameter to a field. If unchecked, + simple getters and setters are not counted in coverage measurements, and + do not result in point deductions."; + }, +/* + { + property = "clover.includes"; + type = shortText; + size = 40; + default = "**"; + name = "Classes to Include in Coverage Measures"; + category = "Basic Settings"; + description = + "Specify the Java file names that should be included in Clover coverage + analysis. Only student class files with names that match the patterns you + list here will be processed by Clover. Patterns are + case-insensitive. Use * as a wildcard character (ANT-style pattern + matching is used)."; + }, + { + property = "clover.excludes"; + type = shortText; + size = 40; + default = "none"; + name = "Classes to Exclude from Coverage Measures"; + category = "Basic Settings"; + description = + "Specify Java file names that should not be processed by + Clover. Any classes that match the \"Classes to Include in Coverage + Measures\" above and also match the patterns you list here + will not be processed by Clover. Patterns are + case-insensitive. Use * as a wildcard character (ANT-style pattern + matching is used). Use \"none\" if you do not wish to use any + exclusion patterns."; + }, +*/ + { + property = studentTestInclude; + type = shortText; + size = 40; + default = "*test *tests"; + name = "Students Test Class Patterns"; + category = "Basic Settings"; + description = + "Specify the Java class names that should be treated as JUnit-style + test cases. Only student classes with names that match the patterns you + list here will be executed as test cases. Patterns are case-insensitive. + Use * as a wildcard character (ANT-style pattern matching is used)."; + }, + { + property = studentTestExclude; + type = shortText; + size = 40; + default = "abstract* *$*"; + name = "Students Test Class Exclusion Patterns"; + category = "Basic Settings"; + description = + "Specify Java class names that should not be treated as JUnit-style + test cases. Any classes that match the \"Student Test Class Patterns\" + above and also match the patterns you list here + will not be treated as executable test cases. Patterns are + case-insensitive. Use * as a wildcard character (ANT-style pattern matching + is used). Use \"none\" if you do not wish to use any exclusion patterns."; + }, + { + property = refTestInclude; + type = shortText; + size = 40; + default = "*"; + name = "Reference Test Class Patterns"; + category = "Basic Settings"; + description = + "Specify the Java class names that should be treated as JUnit-style + test cases when selected as reference tests by an instructor. This + setting is only relevant when an instructor selects an entire directory + or folder of Java classes. In that case, only instructor reference classes + with names that match the patterns you list here will be executed as test + cases. Patterns are case-insensitive. Use * as a wildcard character + (ANT-style pattern matching is used)."; + }, + { + property = refTestExclude; + type = shortText; + size = 40; + default = "abstract* *$*"; + name = "Reference Test Class Exclusion Patterns"; + category = "Basic Settings"; + description = + "Specify Java class names that should not be treated as JUnit-style + test cases when selected as reference tests by an instructor. This + setting is only relevant when an instructor selects an entire directory or + folder of Java classes. In that case, any classes that match the + \"Reference Test Class Patterns\" above and also match the + patterns you list here will not be treated as executable test cases. + Patterns are case-insensitive. Use * as a wildcard character (ANT-style + pattern matching is used)."; + }, + { + property = student.testingsupport.junit4.AdaptiveTimeout.ceiling; + advanced = true; + type = integer; + name = "Default Test Case Time Limit (in ms)"; + category = "Basic Settings"; + description = + "This plug-in provides built-in adaptive detection and termination of + infinite loops that occur within individual test methods. This setting + controls the default timeout before a single test case method is judged as + \"taking too long\", although this amount will be gradually increased up + to a higher maximum value if test methods terminate but come close to + this limit. The default if unset is ten seconds (10000 ms)."; + }, + { + property = student.testingsupport.junit4.AdaptiveTimeout.maximum; + advanced = true; + type = integer; + name = "Default Test Case Maximum Time (in ms)"; + category = "Basic Settings"; + description = + "This plug-in provides built-in adaptive detection and termination of + infinite loops that occur within individual test methods. This setting + controls the maximum allowable timeout before a single test case is judged + as \"taking too long\". The default if unset is twenty seconds (20000 ms)."; + }, + { + property = disableCheckstyle; + type = antBoolean; + name = "Turn Checkstyle Off"; + category = "Basic Settings"; + description = + "Disable Checkstyle for static analysis entirely (no need to upload a + separate configuration file to turn it off)."; + }, + { + property = checkstyleConfig; + advanced = true; + type = file; + fileTypes = ( xml ); + name = "Checkstyle Configuration"; + category = "Basic Settings"; + description = + "An XML file containing a Checkstyle rule configuration (see the + Checksyle + documentation). This plug-in uses Checkstyle v5.6. If you would + like to turn off all Checkstyle checks entirely, use the \"Turn + Checkstyle Off\" option instead."; + }, + { + property = disablePmd; + type = antBoolean; + name = "Turn PMD Off"; + category = "Basic Settings"; + description = + "Disable PMD for static analysis entirely (no need to upload a + separate configuration file to turn it off)."; + }, + { + property = pmdConfig; + advanced = true; + type = file; + fileTypes = ( xml ); + name = "PMD Configuration"; + category = "Basic Settings"; + description = + "An XML file containing a set of PMD rules (see the + PMD + documentation). This plug-in uses PMD v5.0.5. If you owuld like to + turn off all PMD checks entirely, use the \"Turn PMD Off\" options instead."; + }, + { + property = use.comtor; + type = antBoolean; + name = "Run COMTOR Comment Analyzer"; + category = "Basic Settings"; + description = + "Set to true to run the COMTOR Comment Analyzer and include the results + as part of the overall feedback report."; + }, + { + property = staticAnalysisInclude; + type = shortText; + size = 40; + default = "*"; + name = "Classes to Analyze"; + category = "Basic Settings"; + description = + "Specify the Java class names that should be included in Checkstyle and + PMD analysis. Only student classes with names that match the patterns you + list here will be processed by Checkstyle and PMD. Patterns are + case-insensitive. Use * as a wildcard character (ANT-style pattern + matching is used)."; + }, + { + property = staticAnalysisExclude; + type = shortText; + size = 40; + default = "none"; + name = "Classes to Exclude from Analysis"; + category = "Basic Settings"; + description = + "Specify Java class names that should not be processed by + Checkstyle or PMD. Any classes that match the \"Classes to Analyze\" + above and also match the patterns you list here + will not be processed by Checkstyle or PMD. Patterns are + case-insensitive. Use * as a wildcard character (ANT-style pattern + matching is used). Use \"none\" if you do not wish to use any + exclusion patterns."; + }, + { + property = markupProperties; + advanced = true; + type = file; + fileTypes = ( properties ); + name = "Static Analysis Scoring Scheme"; + category = "Basic Settings"; + description = + "A Java properties file containing the point deductions and limits to + use for messages generated by Checkstyle or PMD. The point deductions + are specified in a fairly generic way so they can be used for many + assignments. Deductions in the default scheme are typically 1, 2, or 5 + 'points', which are really simply relative weights. Specify a scaling + factor below to adjust how these weights are translated into point + deductions for a student."; + }, + { + property = toolDeductionScaleFactor; + advanced = true; + type = double; + name = "Static Analysis Deduction Scaling Factor"; + category = "Basic Settings"; + description = + "The Static Analysis Scoring Scheme above defines the point deductions + and limits to use for messages generated by Checkstyle or PMD in a generic + way, with most deductions in the default scheme being 1, 2, or 5 points. + Deductions in the static analysis scoring scheme are multiplied by this + factor to translate them into actual 'point deductions' shown to the + student."; + }, + { + property = hintsLimit; + type = integer; + default = 3; + name = "Hints Limit"; + category = "Basic Settings"; + description = + "Maximum number of hints the student will receive from failed reference + tests."; + }, + { + property = minCoverageLevel; + type = double; + name = "Minimum Test Coverage for Hints"; + category = "Basic Settings"; + description = + "If students are required to submit tests, this value is a minimum test + coverage threshold that must be achieved in order for any hints to be + given. It should be a number between 0.0-100.0 representing the minimum + percent coverage required to see hints."; + }, + { + property = junitErrorsHideHints; + type = boolean; + default = false; + name = "Clean JUnit Tests Required for Hints"; + category = "Basic Settings"; + description = + "If students are required to submit tests, this option requires all test + case classes to be free of PMD-based JUnit style errors, such as failing + to include at least one test method in each test case class, failing + to include assert*() calls in each test case method, or using \"bogus\" + assertions such as assertEquals(1, 1)."; + }, + { + property = hideHintsWithin; + advanced = true; + type = integer; + default = 0; + name = "Hide Hints X Days Before Deadline"; + category = "Basic Settings"; + description = + "Suppress all hints from failed reference tests for submissions within this + many days of the deadline (set to zero for hints to always be visible). + This setting allows the instructor to \"hide\" hints close to the assignment + deadline in an attempt to encourage students to start working earlier."; + }, + { + property = showHintsWithin; + advanced = true; + type = integer; + default = 0; + name = "Show Hints X Days Before Deadline"; + category = "Basic Settings"; + description = + "Show hints (up to the Hints Limit) from failed reference tests for + submissions within this many days of the deadline (only useful when Hide + Hints X Days Before Deadline is non-zero, to restore hints as the + deadline approaches)."; + }, + { + property = wantPDF; + type = boolean; + default = false; + name = "Generate PDF Printouts"; + category = "Basic Settings"; + description = + "Set to true if you wish for a single PDF file containing a pretty-printed + source code printout to be generated from the student's code. The printout + will be downloadable by students, and accessible by TAs during grading. + Note: This option uses both enscript and + ps2pdf as external commands, and requires these programs to + be correctly installed and configured."; + }, +/* + { + property = enscriptStyle; + type = shortText; + size = 15; + default = "msvc"; + name = "PDF Formatting Style (for Enscript)"; + category = "Basic Settings"; + description = + "If you are generating PDF printouts, you can specify the formatting style + used by enscript. This name will be passed to + enscript using its --style= parameter. See your + enscript documentation for more information about the styles that are + supported. Many enscript installations support the following styles: + a2ps, emacs, emacs-verbose, + ifh, and msvc. It is possible to add your + own custom formatting definitions to your enscript installation and then + use your own style name here as well."; + }, +*/ + { + property = wantClassDiagrams; + type = boolean; + default = true; + name = "Generate Class Diagrams"; + category = "Basic Settings"; + description = + "Set to true if you wish to generate class diagrams from students' code. + The diagrams will be viewable by students, and accessible by TAs during + grading. Note: This option uses both doxygen and + dot as external commands, and requires these programs to + be correctly installed and configured in the JavaTddPlugin's global + configuration options."; + }, + { + property = debug; + type = integer; + advanced = true; + default = 0; + name = "Debug Level"; + category = "Developer Settings"; + description = + "Set to a non-zero value for the script to produce debugging output (the + larger the number, the greater the detail, up to about 5). Debugging output + on each grading script run will be e-mailed to the instructor."; + }, + { + property = doNotDelete; + type = antBoolean; + advanced = true; + name = "Preserve Derived Files"; + category = "Developer Settings"; + description = + "Set to true to prevent the plug-in from deleting the derived files it + creates during the build/test process for each submission. Normally, these + files are deleted when a given submission has been completely processed. + This setting is provided for debugging purposes, when one wishes to + inspect the intermediate test driver source code or other derived files."; + }, + { + property = generateHeatmaps; + type = antBoolean; + advanced = true; + name = "Generate Bug Heatmaps"; + category = "Developer Settings"; + description = + "Set to true to generate GZoltar-based defect heatmaps. This option is + experimental and for research use only. Using it will slow down + generation of student feedback."; + }, + { + property = useEnhancedFeedback; + type = boolean; + advanced = true; + default = false; + name = "Use Enhanced Feedback (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to use the new (experimental) enhanced feedback layout."; + }, + { + property = useIndicatorFeedback; + type = boolean; + advanced = true; + default = false; + name = "Use Growth Mindset Feedback (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to use the new (experimental) growth mindset progress feedback."; + }, + { + property = useMaria; + type = boolean; + advanced = true; + default = false; + name = "Use Maria (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to show Maria (experimental), the virtual teaching assistant + chatbot."; + }, + { + property = useMariaExplanations; + type = boolean; + advanced = true; + default = false; + name = "Use Maria Explanations (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to show \"Explain...\" links (experimental) by error messages."; + }, + { + property = useDailyMissions; + type = boolean; + advanced = true; + default = false; + name = "Use Daily Missions (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to give students daily mission challenges (experimental)."; + }, + { + property = showAllTestOutcomes; + type = boolean; + advanced = true; + default = false; + name = "Show All Test Outcomes (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to use the new (experimental) feature to show all test outcomes + instead of limited hints. Students still need to meet the coverage + requirements and other requirements to see test outcomes, but will see + the full table of tests instead of limited hints."; + }, + { + property = useFindBugs; + type = boolean; + advanced = true; + default = false; + name = "Use FindBugs (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to add FindBugs analysis to the static analysis scoring of + the assignment. This will cause FindBugs to be run on student submissions, + and the results will be used to give feedback to students. Not all FindBugs + warnings/errors will be shown--the specific list used is research-driven. + There currently are no controls for changing the scoring scheme or enabling + or disabling specific FindBugs checks."; + }, + { + property = usePit; + type = antBoolean; + advanced = true; + default = false; + name = "Use PIT Mutation Analysis (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to turn on mutation analysis for evaluating student-written + test suites. This is highly experimental and not advised for production + use except in research settings."; + }, + { + property = useEMRN; + type = antBoolean; + advanced = true; + default = false; + name = "Use EMRN Grading (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to turn on EMRN grading scale support instead of points."; + }, + { + property = useJdk11; + type = boolean; + advanced = true; + default = false; + name = "Use Java 11 (Experimental)"; + category = "Developer Settings"; + description = + "Set to true to switch to Java 11."; + } + ); + globalOptions = ( + { + property = doxygenDir; + type = shortText; + size = 40; + name = "Doxygen Directory"; + description = + "The directory on the local server that contains the Doxygen executable. + Doxygen (and Dot, below) are used to generate class diagrams for the code + that students submit; if you do not have a copy of Doxygen or wish to + disable diagram generation across all assignments, you can leave this + field blank. If you are not the user administering this Web-CAT server, + you will need to have your system administrator install this tool and set + this path if you wish to use it."; + }, + { + property = dotDir; + type = shortText; + size = 40; + name = "Dot Directory"; + description = + "The directory on the local server that contains the Dot executable (from + the Graphviz package). Dot (and Doxygen, above) are used to generate + class diagrams for the code that students submit; if you do not have a copy + of Dot or wish to disable diagram generation across all assignments, you can + leave this field blank. If you are not the user administering this Web-CAT + server, you will need to have your system administrator install this tool + and set this path if you wish to use it."; + } + ); +} diff --git a/src/execute.pl b/src/execute.pl index 90b57c3..2798f12 100644 --- a/src/execute.pl +++ b/src/execute.pl @@ -52,6 +52,7 @@ filePattern copyHere htmlEscape + smartHtmlEscape addReportFile scanTo scanThrough @@ -70,6 +71,7 @@ compilerErrorEnhancedMessage setResultDir codingStyleMessageValue + findBugsMessage ); use Web_CAT::Indicators::ProgressTracker; use Web_CAT::Indicators::ProgressCommenter; @@ -101,6 +103,16 @@ if ($useDailyMissions) { $useIndicatorFeedback = 1; } my $showAllTestOutcomes = $cfg->getProperty('showAllTestOutcomes', 0); $showAllTestOutcomes = ($showAllTestOutcomes =~ m/^(true|on|yes|y|1)$/i); +my $useFindBugs = $cfg->getProperty('useFindBugs', 0); +$useFindBugs = ($useFindBugs =~ m/^(true|on|yes|y|1)$/i); +if ($useFindBugs) { $cfg->setProperty('enableFindBugs', 'true'); } +my $usePit = $cfg->getProperty('usePit', 0); +$usePit = ($usePit =~ m/^(true|on|yes|y|1)$/i); +if ($usePit) { $cfg->setProperty('enablePit', 'true'); } +my $useEMRN = $cfg->getProperty('useEMRN', 0); +$useEMRN = ($useEMRN =~ m/^(true|on|yes|y|1)$/i); +my $useJdk11 = $cfg->getProperty('useJdk11', 0); +$useJdk11 = ($useJdk11 =~ m/^(true|on|yes|y|1)$/i); my $allTestOutcomeResults = ''; my $allTestOutcomesLeader = ''; my $progressTracker = new Web_CAT::Indicators::ProgressTracker($cfg); @@ -324,6 +336,10 @@ #------------------------------------------------------- # In addition, some local definitions within this script #------------------------------------------------------- +if ($useJdk11) +{ + $ENV{JAVA_HOME} = '/usr/java/jdk-11'; +} Web_CAT::Utilities::initFromConfig($cfg); if (defined($ENV{JAVA_HOME})) { @@ -333,6 +349,10 @@ . $Web_CAT::Utilities::PATH_SEPARATOR . $ENV{PATH}; } +# overide TMPDIR to keep temp files local +$ENV{TMPDIR} = "${workingDir}/local_tmp"; +if (! -d "$ENV{TMPDIR}") { mkdir("$ENV{TMPDIR}"); } + die "ANT_HOME environment variable is not set! (Should come from ANTForPlugins)" if !defined($ENV{ANT_HOME}); $ENV{PATH} = @@ -373,6 +393,7 @@ my $coverageGoal = $cfg->getProperty('coverageGoal', 100.0); if ($coverageGoal <= 0) { $coverageGoal = 100; } +my $printableCoverageGoal = $coverageGoal; if ($coverageGoal >= 1) { $coverageGoal /= 100.0; } my $useXvfb = @@ -386,11 +407,19 @@ $cfg->getProperty('allStudentTestsMustPass', 0); $allStudentTestsMustPass = ($allStudentTestsMustPass =~ m/^(true|on|yes|y|1)$/i); +my $includeStudentTestsInGrading = + $cfg->getProperty('includeStudentTestsInGrading', 0); +$includeStudentTestsInGrading = + ($includeStudentTestsInGrading =~ m/^(true|on|yes|y|1)$/i); my $studentsMustSubmitTests = $cfg->getProperty('studentsMustSubmitTests', 0); $studentsMustSubmitTests = ($studentsMustSubmitTests =~ m/^(true|on|yes|y|1)$/i); -if (!$studentsMustSubmitTests) { $allStudentTestsMustPass = 0; } +if (!$studentsMustSubmitTests) +{ + $allStudentTestsMustPass = 0; + $includeStudentTestsInGrading = 0; +} my $includeTestSuitesInCoverage = $cfg->getProperty('includeTestSuitesInCoverage', 0); $includeTestSuitesInCoverage = @@ -908,7 +937,7 @@ sub generateCompilerErrorWarningStruct $fileName =~ s,\\,/,go; my $lineNum = $fileDetails[1]; my $codeLines = extractAboveBelowLinesOfCode($fileName, $lineNum); - $fileName =~ s,^\Q$workingDir/\E,,i; + $fileName =~ s,^.*\Q$workingDir/\E,,i; # For compiler warning we dont have enhanced messages # For "cannot find symbol" errors, "addCannotFindSymbolStruct" computes @@ -1281,7 +1310,7 @@ sub addCannotFindSymbolStruct #============================================================================= -# Load checkstyle and PMD reports into internal data structures +# Load checkstyle, PMD, and FindBugs reports into internal data structures #============================================================================= # The configuration file for scoring tool messages @@ -1603,6 +1632,7 @@ sub markStyleSection # violation: the XML::Smart structure referring to the violation # (used for error message printing only) # +my $debugTracking = 0; sub trackMessageInstance { croak 'usage: recordPMDMessageStats(rule, fileName, violation)' @@ -1616,6 +1646,16 @@ sub trackMessageInstance * $toolDeductionScaleFactor; my $overLimit = 0; + if ($debug > 2 || $debugTracking) + { + print "trackMessageInstance($rule, $fileName, ...)\n"; + my $msg = $violation->data_pointer(noheader => 1, nometagen => 1); + if (defined $msg) + { + print $msg; + } + } + if (!$violation->{line}->content && $violation->{endline}->content) { @@ -1844,7 +1884,7 @@ sub trackMessageInstance next if ($file->{name}->null); my $fileName = $file->{name}->content; $fileName =~ s,\\,/,go; - $fileName =~ s,^\Q$workingDir/\E,,i; + $fileName =~ s,^.*\Q$workingDir/\E,,i; if (!defined $codeMarkupIds{$fileName}) { $codeMarkupIds{$fileName} = ++$numCodeMarkups; @@ -1875,7 +1915,7 @@ sub trackMessageInstance next if ($file->{name}->null); my $fileName = $file->{name}->content; $fileName =~ s,\\,/,go; - $fileName =~ s,^\Q$workingDir/\E,,i; + $fileName =~ s,^.*\Q$workingDir/\E,,i; if (!defined $codeMarkupIds{$fileName}) { $codeMarkupIds{$fileName} = ++$numCodeMarkups; @@ -1891,8 +1931,137 @@ sub trackMessageInstance } } + my $findBugsLog = "$resultDir/findbugs.xml"; + if (-f $findBugsLog) + { + my $fb = XML::Smart->new($findBugsLog); +# print "parsed findbugs =\n", $fb->data(noheader => 1, nometagen => 1), "\n"; + # First, pull long error details + my %msgs = (); + foreach my $pat (@{ $fb->{BugCollection}{BugPattern} }) + { + my $detail = $pat->{Details}->content; + $detail =~ s/^\s+|\s+$//gso; + $detail =~ s/^

\s*|\s*<\/p>$//gso; + $detail =~ s/ < / < /go; + $detail =~ s/ <= / <= /go; + $detail =~ s/ > / > /go; + $detail =~ s/ >= / >= /go; + $detail =~ s/x&x/x&x/go; + $detail = '

' . $detail . '

'; + $msgs{$pat->{type}->content} = $detail; + } + foreach my $bug (@{ $fb->{BugCollection}{BugInstance} }) + { + $bug->{FindBugsCategory} = $bug->{category}; + $bug->{tool} = 'FindBugs'; + my $type = $bug->{type}->content; + my $msg = ''; + if (!$bug->{LongMessage}->null) + { + $msg = $bug->{LongMessage}->content . '.'; + } + elsif (!$bug->{ShortMessage}->null) + { + $msg = $bug->{ShortMessage}->content . '.'; + } + $msg = htmlEscape($msg); + + # highlight variable name, if there is one + my $v = $bug->{LocalVariable}{Message}; + if (!$v->null) + { + $v = htmlEscape($v->content); + if ($v =~ m/^\s*Did/so) + { + $v =~ s/variable\s+(\S+)\?\s*$/variable $1<\/code>?/; + $msg .= ' ' . $v; + } + elsif ($v =~ m/named\s+(\S+)\s*$/o) + { + $v = $1; + $msg =~ s/\Q$v\E/$v<\/code>/g; + } + } + + # highlight method name, if there is one + my $method = $bug->{Method}{Message}; + if (!$method->null) + { + $method = htmlEscape($method->content); + if ($method =~ m/[Mm]ethod\s+(\S+)\s*$/o) + { + $method = $1; + $msg =~ s/\Q$method\E/$method<\/code>/g; + } + } + + # Add field message, if there is one + my $field = $bug->{Field}{Message}; + if (!$field->null) + { + my $fmsg = htmlEscape($field->content); + if ($fmsg =~ m/^\s*Did/so) + { + $fmsg =~ s/field\s+(\S+)\?\s*$/field $1<\/code>?/; + $msg .= ' ' . $fmsg; + } + elsif ($fmsg =~ m/[Ff]ield\s+(\S+)\s*$/o) + { + $fmsg = $1; + $msg =~ s/\Q$fmsg\E/$fmsg<\/code>/g; + } + } + + my $fileName = ''; + $bug->{beginline} = + $bug->{SourceLine}{start}->content + || $bug->{Method}{SourceLine}{start}->content + || $bug->{Field}{SourceLine}{start}->content + || $bug->{Class}{SourceLine}{start}->content + || 1; + $bug->{endline} = + $bug->{SourceLine}{end}->content + || $bug->{Method}{SourceLine}{end}->content + || $bug->{Field}{SourceLine}{end}->content + || $bug->{Class}{SourceLine}{end}->content + || $bug->{beginline}->content; + $fileName = + $bug->{SourceLine}{sourcepath}->content + || $bug->{Method}{SourceLine}{sourcepath}->content + || $bug->{Field}{SourceLine}{sourcepath}->content + || $bug->{Class}{SourceLine}{sourcepath}->content; + $fileName =~ s,\\,/,go; + $fileName =~ s,^.*\Q$workingDir/\E,,i; + if ($fileName ne '' && ! -f $fileName && -f 'src/' . $fileName) + { + $fileName = 'src/' . $fileName; + } + if ($msg ne '') { $msg = '

' . $msg . '

'; } + my $detail = findBugsMessage($type); + if ($detail eq '' && defined $msgs{$type}) + { + $detail = $msgs{$type}; + } + $msg .= $detail; + if ($msg ne '') + { + $msg =~ s/ / /go; + $bug->{message} = $msg; + } + # print "parsed rule $type in $fileName:\n"; + # print $bug->data_pointer(noheader => 1, nometagen => 1), "\n"; + if ($fileName ne '') + { + trackMessageInstance($type, $fileName, $bug); + } + } + } + if ($debug > 1) { + # dump message stats + print "==========\ndumping message stats structure\n==========\n"; my $msg = $messageStats->data(noheader => 1, nometagen => 1); if (defined $msg) { @@ -2025,7 +2194,7 @@ sub trackMessageInstance { $runtimeScoreWithoutCoverage = 0; } - else + elsif ($includeStudentTestsInGrading) { $runtimeScoreWithoutCoverage *= $status{'studentTestResults'}->testPassRate; @@ -2241,7 +2410,7 @@ sub extractExemptLines #============================================================================= # post-process generated HTML files #============================================================================= -my $jacoco = (-f "$resultDir/jacoco.xml") +my $jacoco = (!$usePit && -f "$resultDir/jacoco.xml") ? XML::Smart->new("$resultDir/jacoco.xml") : undef; @@ -2558,6 +2727,124 @@ sub processStatementsUncovered } if (!$buildFailed) # $can_proceed) +{ +if ($usePit) +{ + $gradedElements = 0; + $gradedElementsCovered = 0; + my $pit = (-f "$resultDir/mutations.xml") + ? XML::Smart->new("$resultDir/mutations.xml") + : undef; + my %coveredByFile = (); + if (defined $pit) + { + for my $m (@{$pit->{mutations}{mutation}}) + { + my $fileName = $m->{sourceFile}; + if (! -f $fileName && -f 'src/' . $fileName) + { + $fileName = 'src/' . $fileName; + } + $gradedElements++; + if (defined $coveredByFile{$fileName}) + { + $coveredByFile{$fileName}{elements}++; + } + else + { + $coveredByFile{$fileName} = { elements => 1, elementsCovered => 0 }; + } + if ($m->{detected}->content eq 'true') + { + $gradedElementsCovered++; + $coveredByFile{$fileName}{elementsCovered}++; + } + else + { + my $className = $m->{mutatedClass}; + my $num = $m->{lineNumber}; + my $desc = $m->{description}->content; + + my $codeMarkupNo; + if (defined $codeMarkupIds{$fileName}) + { + $codeMarkupNo = $codeMarkupIds{$fileName}; + } + else + { + $codeMarkupNo = ++$numCodeMarkups; + $codeMarkupIds{$fileName} = $codeMarkupNo; + } + if (!defined $codeMessages{$fileName}) + { + $codeMessages{$fileName} = {}; + } + my $msgs = $codeMessages{$fileName}; + if (!defined $msgs->{$num}) + { + $msgs->{$num} = {}; + } + $msgs->{$num}{category} = 'coverage'; + $msgs->{$num}{coverage} = 'e'; + if (defined $msgs->{$num}->{message}) + { + $msgs->{$num}->{message} .= '; ' . $desc; + } + else + { + $msgs->{$num}->{message} = 'Uncovered mutation(s): ' . $desc; + } + + } + } + } + else + { + $gradedElements = 1; + } + if ($gradedElements == 0) + { + # No mutants? + $gradedElements = 1; + $gradedElementsCovered = 1; + } + + # set code markup properties + $cfg->setProperty("statElementsLabel", "Mutants Detected"); + my %fileDeductionProperties = (); + my $ptsPerUncovered = 0.0; + if ($studentsMustSubmitTests) + { + if ($gradedElements > 0 + && $runtimeScoreWithoutCoverage > 0 + && ($gradedElementsCovered * 1.0 / $gradedElements) + < $coverageGoal) + { + $ptsPerUncovered = -1.0 / + $gradedElements + * $runtimeScoreWithoutCoverage + * $coverageGoal; + } + } + for my $fileName (keys %coveredByFile) + { + my $codeMarkupNo = $codeMarkupIds{$fileName}; + my $myElements = $coveredByFile{$fileName}{elements}; + my $myElementsCovered = $coveredByFile{$fileName}{elementsCovered}; + $cfg->setProperty("codeMarkup${codeMarkupNo}.elements", + $myElements); + $cfg->setProperty("codeMarkup${codeMarkupNo}.elementsCovered", + $myElementsCovered); + $cfg->setProperty("codeMarkup${codeMarkupNo}.sourceFileName", + $fileName); + $cfg->setProperty("codeMarkup${codeMarkupNo}.deductions", + ($myElements - $myElementsCovered) * $ptsPerUncovered + + 0 - $messageStats->{file}->{$fileName}->{pts}->content); + $cfg->setProperty("codeMarkup${codeMarkupNo}.remarks", + (0 + $messageStats->{file}->{$fileName}->{remarks}->content)); + } +} +else { if (defined $jacoco) { @@ -3082,7 +3369,7 @@ sub processStatementsUncovered processStatementsUncovered(); } - +} } $cfg->setProperty('numCodeMarkups', $numCodeMarkups); @@ -3107,7 +3394,14 @@ sub processStatementsUncovered # Only generate this section if compilation was successful if (!defined $status{'studentTestResults'}) { + if ($studentsMustSubmitTests) + { $sectionTitle .= "(No Test Results!)"; + } + else + { + $sectionTitle .= "(No Test Results!)"; + } # Mark results in testingSectionStatus as well so that we can fill # the radial bar. @@ -3115,7 +3409,14 @@ sub processStatementsUncovered } elsif ($status{'studentTestResults'}->testsExecuted == 0) { + if ($studentsMustSubmitTests) + { $sectionTitle .= "(No Tests Submitted!)"; + } + else + { + $sectionTitle .= "(No Tests Submitted!)"; + } $testingSectionStatus{'resultsPercent'} = 0; } elsif ($status{'studentTestResults'}->allTestsPass) @@ -3125,7 +3426,14 @@ sub processStatementsUncovered } else { + if ($studentsMustSubmitTests) + { $sectionTitle .= "($studentCasesPercent%)"; + } + else + { + $sectionTitle .= "($studentCasesPercent%)"; + } $testingSectionStatus{'resultsPercent'} = $studentCasesPercent; } @@ -3134,7 +3442,8 @@ sub processStatementsUncovered ++$expSectionId, $status{'studentTestResults'}->allTestsPass); - if ($allStudentTestsMustPass + if ($studentsMustSubmitTests + && $allStudentTestsMustPass && $status{'studentTestResults'}->testsFailed > 0) { $status{'feedback'}->print( @@ -3186,16 +3495,17 @@ sub processStatementsUncovered $status{'feedback'}->endFeedbackSection; } - if ($gradedElements > 0 - || (defined $status{'studentTestResults'} - && $status{'studentTestResults'}->testsExecuted > 0)) + if ($gradedElements > 0) +# || (defined $status{'studentTestResults'} +# && $status{'studentTestResults'}->testsExecuted > 0)) { $codeCoveragePercent = 0; if ($gradedElements > 0) { $codeCoveragePercent = int(($gradedElementsCovered * 1.0 / $gradedElements) - / $coverageGoal * 100.0 + 0.5); + # / $coverageGoal + * 100.0 + 0.5); if ($codeCoveragePercent > 100) { $codeCoveragePercent = 100; } if (($gradedElementsCovered * 1.0 / $gradedElements) < $coverageGoal && $codeCoveragePercent == 100) @@ -3210,16 +3520,17 @@ sub processStatementsUncovered if (!$useEnhancedFeedback) { - $sectionTitle = "Code Coverage from Your Tests "; + $sectionTitle = ($usePit ? "Mutants Detected by" : "Code Coverage from") + . " Your Tests "; if ($gradedElements == 0) { $sectionTitle .= "(No Coverage!)"; } - elsif (($gradedElementsCovered * 1.0 / $gradedElements) - >= $coverageGoal) - { - $sectionTitle .= "(100%)"; - } +# elsif (($gradedElementsCovered * 1.0 / $gradedElements) +# >= $coverageGoal) +# { +# $sectionTitle .= "(100%)"; +# } else { $sectionTitle .= "($codeCoveragePercent%)"; @@ -3228,7 +3539,8 @@ sub processStatementsUncovered $status{'feedback'}->startFeedbackSection( $sectionTitle, ++$expSectionId, 1); - $status{'feedback'}->print("

Code Coverage: "); + $status{'feedback'}->print("

" . + ($usePit ? "Mutants Detected" : "Code Coverage") .": "); if ($codeCoveragePercent < 100) { $status{'feedback'}->print( @@ -3241,9 +3553,10 @@ sub processStatementsUncovered my $descr = $cfg->getProperty("statElementsLabel", "Methods Executed"); $descr =~ tr/A-Z/a-z/; - $descr =~ s/\s*executed\s*$//; + $descr =~ s/\s*Detected\s*$/ detected/; + $descr =~ s/\s*executed\s*$/ exercised/; $status{'feedback'}->print(< (percentage of $descr exercised by your tests)

+ (percentage of $descr by your tests)

You can improve your testing by looking for any lines highlighted in this color in your code listings above. Such lines have not been sufficiently @@ -3827,6 +4140,9 @@ sub generateCompleteErrorStruct my $errorMessage = shift; my $enhancedMessage = shift || ''; + carp "no line number provided" + if !defined($lineNum) || $lineNum eq ''; + my $codeLines = extractAboveBelowLinesOfCode($fileName, $lineNum); my $errorStruct = expandedMessage->new( @@ -3870,7 +4186,10 @@ sub generateCompleteErrorStruct #print $c->{rule}->content; #print("\n"); - my $lineNum = $c->{line}->content; + my $lineNum = + $c->{line}->null + ? $c->{beginline}->content + : $c->{line}->content; # This is the case of "TestsHaveAssertions" rule from pmd # where beginline is the one which contains the declaration @@ -3881,6 +4200,12 @@ sub generateCompleteErrorStruct { $lineNum = $c->{beginline}->content; } + + if (!defined($lineNum) || $lineNum eq '' ) + { + print "no line number found in:\n", + $c->data_pointer(noheader => 1, nometagen => 1), "\n"; + } my $codingStyleStruct = generateCompleteErrorStruct( $ff, $lineNum, $message); @@ -4913,7 +5238,7 @@ sub groupStructsByFileName #============================================================================= # generate score explanation for student #============================================================================= -if ($can_proceed && $studentsMustSubmitTests) +if ($can_proceed && $studentsMustSubmitTests && !$useEMRN) { my $scoreToTenths = int($runtimeScore * 10 + 0.5) / 10; my $possible = int($maxCorrectnessScore * 10 + 0.5) / 10; @@ -4922,17 +5247,36 @@ sub groupStructsByFileName . "($scoreToTenths/$possible)", ++$expSectionId, 1); + my $covLabel = ($usePit ? "Mutants detected by" : "Code coverage from"); $status{'feedback'}->print(< Results from running your tests: $studentCasesPercent% -Code coverage from your tests: +$covLabel your tests: $codeCoveragePercent% +EOF + if ($coverageGoal < 1) + { + $status{'feedback'}->print(<Coverage goal for full credit: +$printableCoverageGoal% +EOF + } + $status{'feedback'}->print(<Estimate of problem coverage: $instructorCasesPercent% score = -$studentCasesPercent% -* $codeCoveragePercent% +EOF + if ($includeStudentTestsInGrading) + { + $status{'feedback'}->print(" $studentCasesPercent% * "); + } + $status{'feedback'}->print("$codeCoveragePercent% "); + if ($coverageGoal < 1) + { + $status{'feedback'}->print("/ $printableCoverageGoal% "); + } + $status{'feedback'}->print(< @@ -5086,6 +5430,19 @@ sub groupStructsByFileName # Update and rewrite properties to reflect status #============================================================================= +sub smartHtmlEscapeAndPeel +{ + my $msg = smartHtmlEscape(shift); + + if (defined $msg) + { + $msg =~ s/^\s*<[Pp](\s+[^>]*)?>//o; + $msg =~ s/\s*<\/[Pp]>\s*$//o; + } + + return $msg; +} + # Student feedback # ----------- { @@ -5142,7 +5499,8 @@ sub computeExpandSectionId } if ($testingSectionStatus{'errors'} == 0 || - $testingSectionStatus{'failures'} == 0 || + ($includeStudentTestsInGrading && + $testingSectionStatus{'failures'} == 0) || $testingSectionStatus{'methodsUncovered'} == 0 || $testingSectionStatus{'statementsUncovered'} == 0 || $testingSectionStatus{'conditionsUncovered'} == 0) @@ -5332,7 +5690,7 @@ sub computeExpandSectionId { print IMPROVEDFEEDBACKFILE '

', $errorStruct->entityName, '

', - htmlEscape($errorStruct->errorMessage), ' '; + smartHtmlEscapeAndPeel($errorStruct->errorMessage), ' '; my $msg = ''; @@ -5376,7 +5734,8 @@ sub computeExpandSectionId if ($errorStruct->enhancedMessage) { print IMPROVEDFEEDBACKFILE '

', - htmlEscape($errorStruct->enhancedMessage), '

'; + smartHtmlEscapeAndPeel($errorStruct->enhancedMessage), + '

'; } } } @@ -5393,7 +5752,8 @@ sub computeExpandSectionId # Testing Section my $showTesting = 1; my $testingMsg = ''; -if ($status{'studentTestResults'}->testsExecuted == 0) +if (!defined($status{'studentTestResults'}) || + $status{'studentTestResults'}->testsExecuted == 0) { $showTesting = 0; $expandSectionId = 2; @@ -5505,7 +5865,8 @@ sub computeExpandSectionId foreach my $errorStruct (@{$testingSectionExpanded{$element}}) { print IMPROVEDFEEDBACKFILE '

', $errorStruct->entityName, '

', - '

', htmlEscape($errorStruct->errorMessage), + '

', + smartHtmlEscapeAndPeel($errorStruct->errorMessage), ' '; my $msg = ''; @@ -5563,7 +5924,8 @@ sub computeExpandSectionId if ($errorStruct->enhancedMessage) { print IMPROVEDFEEDBACKFILE '

', - htmlEscape($errorStruct->enhancedMessage), '

'; + smartHtmlEscapeAndPeel($errorStruct->enhancedMessage), + '

'; } } } @@ -5586,7 +5948,8 @@ sub computeExpandSectionId $behaviorMsg = '

Fix Unit Test Coding Problems ' . '(see above) for behavioral analysis.

'; } - elsif ($status{'studentTestResults'}->testsExecuted == 0) + elsif (!defined $status{'studentTestResults'} + || $status{'studentTestResults'}->testsExecuted == 0) { $showBehavior = 0; $behaviorMsg = '

Your own software tests must be included for ' @@ -5732,7 +6095,8 @@ sub computeExpandSectionId foreach my $errorStruct (@{$behaviorSectionExpanded{$element}}) { print IMPROVEDFEEDBACKFILE '

', $errorStruct->entityName, '

', - '

', htmlEscape($errorStruct->errorMessage), ' '; + '

', + smartHtmlEscapeAndPeel($errorStruct->errorMessage), ' '; my $msg = Web_CAT::Maria::explainButton( runtimeErrorHintKey($errorStruct->errorMessage), $useMariaExplanations); @@ -5763,7 +6127,8 @@ sub computeExpandSectionId if ($errorStruct->enhancedMessage) { print IMPROVEDFEEDBACKFILE '

', - htmlEscape($errorStruct->enhancedMessage), '

'; + smartHtmlEscapeAndPeel($errorStruct->enhancedMessage), + '

'; } } } @@ -5874,7 +6239,8 @@ sub computeExpandSectionId foreach my $errorStruct (@{$styleSectionExpanded{$element}}) { print IMPROVEDFEEDBACKFILE '

', $errorStruct->entityName, '

', - '

', htmlEscape($errorStruct->errorMessage), + '

', + smartHtmlEscapeAndPeel($errorStruct->errorMessage), "

\n"; my @linesOfCode = split /\n/, $errorStruct->linesOfCode; @@ -5903,7 +6269,8 @@ sub computeExpandSectionId if ($errorStruct->enhancedMessage) { print IMPROVEDFEEDBACKFILE '

', - htmlEscape($errorStruct->enhancedMessage), '

'; + smartHtmlEscapeAndPeel($errorStruct->enhancedMessage), + '

'; } } } @@ -5942,6 +6309,75 @@ sub computeExpandSectionId addReportFileWithStyle($cfg, $antLogRelative, "text/plain", 0, "admin"); } +if ($useEMRN) +{ + my $maxPossible = $maxCorrectnessScore + $maxToolScore; + my $rawScore = $runtimeScore + $staticScore; + my $rawPct = $rawScore / $maxPossible; + my $subTime = $cfg->getProperty('submissionTimestamp', 0); + my $dueTime = $cfg->getProperty('dueDateTimestamp', $subTime); + + # Meets expectations + if ($maxPossible >= 100 + && $rawPct >= 0.95 + && $runtimeScore / $maxCorrectnessScore >= 0.95 + && $staticScore / $maxToolScore >= 0.95 + && $subTime <= $dueTime) + { + $staticScore = $maxToolScore; + $runtimeScore = $maxCorrectnessScore; + $cfg->setProperty('score.category', 'Excellent'); + } + elsif ($rawPct >= 0.85 + && $runtimeScore / $maxCorrectnessScore >= 0.85 + && $staticScore / $maxToolScore >= 0.90) + { + if ($maxPossible < 100) + { + $staticScore = $maxToolScore; + $runtimeScore = $maxCorrectnessScore; + } + elsif ($staticScore == $maxToolScore) + { + $runtimeScore = $maxPossible * 0.8 - $staticScore; + } + elsif ($runtimeScore == $maxCorrectnessScore) + { + $staticScore = $maxPossible * 0.8 - $runtimeScore; + } + else + { + $staticScore = 0.8 * $maxToolScore; + $runtimeScore = 0.8 * $maxCorrectnessScore; + } + $cfg->setProperty('score.category', 'Meets Expectations'); + } + elsif ($rawPct > 0) + { + my $target = 50; # Should be programmable + if ($staticScore == $maxToolScore) + { + $runtimeScore = $target - $staticScore; + } + elsif ($runtimeScore == $maxCorrectnessScore) + { + $staticScore = $target - $runtimeScore; + } + else + { + $staticScore = $target / 2; + $runtimeScore = $target / 2; + } + $cfg->setProperty('score.category', 'Revision Needed'); + } + else + { + $runtimeScore = 0; + $staticScore = 0; + $cfg->setProperty('score.category', 'Not Assessable'); + } +} + $cfg->setProperty('score.correctness', $runtimeScore); $cfg->setProperty('score.tools', $staticScore ); $cfg->setProperty('expSectionId', $expSectionId); diff --git a/src/findbugs/README.md b/src/findbugs/README.md new file mode 100644 index 0000000..c05538a --- /dev/null +++ b/src/findbugs/README.md @@ -0,0 +1,9 @@ +FindBugs release for use within the JavaTddPlugin. The plugin is currently +using version 3.0.1 of FindBugs, which should be placed in this file to +build the plugin. + +Download the findbugs-3.0.1.zip file (or the newest version) from: + +http://findbugs.sourceforge.net/downloads.html + +Unzip it and copy the contents of the findbugs-3.0.1/ folder into this folder. diff --git a/src/findbugs/filter.xml b/src/findbugs/filter.xml new file mode 100644 index 0000000..de4f700 --- /dev/null +++ b/src/findbugs/filter.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/jacoco/README.txt b/src/jacoco/README.txt index 42990ca..b157512 100644 --- a/src/jacoco/README.txt +++ b/src/jacoco/README.txt @@ -8,4 +8,4 @@ Use the latest version from: https://www.jacoco.org/jacoco/ -The current official build of this plug-in uses v 0.8.5. +The current official build of this plug-in uses v 0.8.6. diff --git a/src/java.policy b/src/java.policy index 08a55ef..7f58d28 100644 --- a/src/java.policy +++ b/src/java.policy @@ -1,5 +1,13 @@ // Standard extensions get all permissions by default +grant codeBase "file:${java.home}/-" { + permission java.security.AllPermission; +}; + +grant codeBase "file:/Web-CAT/Plugins/JavaTddPlugin/src/-" { + permission java.security.AllPermission; +}; + grant codeBase "file:${java.home}/lib/ext/*" { permission java.security.AllPermission; }; @@ -26,6 +34,9 @@ grant { // for JES permission java.io.FilePermission "${user.home}${/}JESConfig.txt", "read"; + + // for jacoco + permission java.net.SocketPermission "*", "resolve"; }; // default permissions granted to all domains @@ -58,7 +69,7 @@ grant { permission java.net.SocketPermission "google.com", "connect"; permission java.net.SocketPermission "www.google.com", "connect"; - // "standard" properies that can be read by anyone + // "standard" properties that can be read by anyone permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; diff --git a/src/markup.properties b/src/markup.properties index 8338b85..182199b 100644 --- a/src/markup.properties +++ b/src/markup.properties @@ -277,3 +277,201 @@ ProhibitedGreenfootImport.category = Error ProhibitedGreenfootImport.deduction = 5 AvoidAccessibilityAlteration.group = coding AvoidAccessibilityAlteration.deduction = 5 + + +# FindBugs BAD_PRACTICE +# -------------------------------------------- +VA_FORMAT_STRING_USES_NEWLINE.group = codingMinor +ES_COMPARING_STRINGS_WITH_EQ.group = codingMinor +EQ_COMPARETO_USE_OBJECT_EQUALS.group = codingMinor +DM_EXIT.group = codingMinor +RR_NOT_CHECKED.group = codingMinor +NM_CLASS_NAMING_CONVENTION.group = codingMinor +EQ_GETCLASS_AND_CLASS_CONSTANT.group = codingMinor +NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT.group = codingMinor +RV_RETURN_VALUE_IGNORED_BAD_PRACTICE.group = codingMinor +BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS.group = codingMinor +ES_COMPARING_PARAMETER_STRING_WITH_EQ.group = codingMinor +SE_COMPARATOR_SHOULD_BE_SERIALIZABLE.group = codingMinor +OS_OPEN_STREAM.group = codingMinor +CO_SELF_NO_OBJECT.group = codingMinor +CO_COMPARETO_INCORRECT_FLOATING.group = codingMinor +SR_NOT_CHECKED.group = codingMinor +NP_TOSTRING_COULD_RETURN_NULL.group = codingMinor +HE_EQUALS_NO_HASHCODE.group = codingMinor +NM_CLASS_NOT_EXCEPTION.group = codingMinor +HE_INHERITS_EQUALS_USE_HASHCODE.group = codingMinor +CNT_ROUGH_CONSTANT_VALUE.group = codingMinor +IT_NO_SUCH_ELEMENT.group = codingMinor +CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE.group = codingMinor +DMI_RANDOM_USED_ONLY_ONCE.group = codingMinor +EQ_SELF_NO_OBJECT.group = codingMinor +NM_FIELD_NAMING_CONVENTION.group = codingMinor +DE_MIGHT_IGNORE.group = codingMinor +EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS.group = codingMinor +NM_SAME_SIMPLE_NAME_AS_SUPERCLASS.group = codingMinor +NP_BOOLEAN_RETURN_NULL.group = codingMinor +RC_REF_COMPARISON_BAD_PRACTICE.group = codingMinor +RV_NEGATING_RESULT_OF_COMPARETO.group = codingMinor +CN_IDIOM.group = codingMinor +NM_SAME_SIMPLE_NAME_AS_INTERFACE.group = codingMinor +DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION.group = codingMinor +ME_ENUM_FIELD_SETTER.group = codingMinor +SE_BAD_FIELD.group = codingMinor +RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN.group = codingMinor +SE_BAD_FIELD_STORE.group = codingMinor +CN_IDIOM_NO_SUPER_CALL.group = codingMinor +FI_EXPLICIT_INVOCATION.group = codingMinor +NM_VERY_CONFUSING_INTENTIONAL.group = codingMinor +SE_NONSTATIC_SERIALVERSIONID.group = codingMinor + + +# FindBugs CORRECTNESS +# -------------------------------------------- +IJU_SETUP_NO_SUPER.group = coding +UWF_UNWRITTEN_FIELD.group = coding +UWF_NULL_FIELD.group = coding +EC_BAD_ARRAY_COMPARE.group = coding +NP_UNWRITTEN_FIELD.group = coding +NP_NULL_ON_SOME_PATH_EXCEPTION.group = coding +NP_NONNULL_PARAM_VIOLATION.group = coding +EC_UNRELATED_TYPES.group = coding +UR_UNINIT_READ.group = coding +NP_NULL_ON_SOME_PATH.group = coding +RV_ABSOLUTE_VALUE_OF_RANDOM_INT.group = coding +RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE.group = coding +DMI_INVOKING_TOSTRING_ON_ARRAY.group = coding +EQ_SELF_USE_OBJECT.group = coding +RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE.group = coding +IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN.group = coding +NP_NULL_PARAM_DEREF_NONVIRTUAL.group = coding +RV_RETURN_VALUE_IGNORED.group = coding +IL_INFINITE_RECURSIVE_LOOP.group = coding +SA_FIELD_SELF_ASSIGNMENT.group = coding +RpC_REPEATED_CONDITIONAL_TEST.group = coding +NP_ALWAYS_NULL.group = coding +NP_NULL_PARAM_DEREF.group = coding +RC_REF_COMPARISON.group = coding +EC_NULL_ARG.group = coding +SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH.group = coding +NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH.group = coding +RV_01_TO_INT.group = coding +INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE.group = coding +EC_ARRAY_AND_NONARRAY.group = coding +RV_EXCEPTION_NOT_THROWN.group = coding +IL_INFINITE_LOOP.group = coding +NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS.group = coding +BIT_ADD_OF_SIGNED_BYTE.group = coding +BC_IMPOSSIBLE_INSTANCEOF.group = coding +EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC.group = coding +SA_LOCAL_SELF_COMPARISON.group = coding +RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION.group = coding +GC_UNRELATED_TYPES.group = coding +NM_VERY_CONFUSING.group = coding +RANGE_ARRAY_INDEX.group = coding +VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED.group = coding +ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL.group = coding +BIT_IOR_OF_SIGNED_BYTE.group = coding +BC_IMPOSSIBLE_CAST.group = coding +ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND.group = coding +BC_IMPOSSIBLE_DOWNCAST.group = coding +HE_USE_OF_UNHASHABLE_CLASS.group = coding +DLS_OVERWRITTEN_INCREMENT.group = coding +NP_GUARANTEED_DEREF.group = coding +NP_NULL_INSTANCEOF.group = coding +SA_FIELD_SELF_COMPARISON.group = coding +EC_UNRELATED_CLASS_AND_INTERFACE.group = coding +IJU_TEARDOWN_NO_SUPER.group = coding +NM_BAD_EQUAL.group = coding +EQ_ALWAYS_FALSE.group = coding +SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD.group = coding +FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER.group = coding +MF_CLASS_MASKS_FIELD.group = coding +EC_UNRELATED_TYPES_USING_POINTER_EQUALITY.group = coding +DLS_DEAD_LOCAL_INCREMENT_IN_RETURN.group = coding +VA_FORMAT_STRING_BAD_CONVERSION.group = coding +VA_FORMAT_STRING_ILLEGAL.group = coding +EQ_ALWAYS_TRUE.group = coding +QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT.group = coding +INT_BAD_COMPARISON_WITH_SIGNED_BYTE.group = coding +EQ_COMPARING_CLASS_NAMES.group = coding +NP_CLOSING_NULL.group = coding +NM_LCASE_TOSTRING.group = coding +RV_ABSOLUTE_VALUE_OF_HASHCODE.group = coding +NP_ALWAYS_NULL_EXCEPTION.group = coding +VA_FORMAT_STRING_MISSING_ARGUMENT.group = coding +EC_INCOMPATIBLE_ARRAY_COMPARE.group = coding +HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS.group = coding +RANGE_ARRAY_LENGTH.group = coding +BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY.group = coding +RANGE_ARRAY_OFFSET.group = coding +RANGE_STRING_INDEX.group = coding +SA_FIELD_SELF_COMPUTATION.group = coding +NM_METHOD_CONSTRUCTOR_CONFUSION.group = coding +NM_WRONG_PACKAGE.group = coding +SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW.group = coding +DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY.group = coding +DMI_VACUOUS_SELF_COLLECTION_CALL.group = coding +ICAST_BAD_SHIFT_AMOUNT.group = coding +UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS.group = coding +VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED.group = coding +DM_INVALID_MIN_MAX.group = coding +RE_POSSIBLE_UNINTENDED_PATTERN.group = coding +DLS_DEAD_STORE_OF_CLASS_LITERAL.group = coding +VA_FORMAT_STRING_BAD_CONVERSION_FROM_ARRAY.group = coding + + +# FindBugs STYLE +# -------------------------------------------- +DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD.group = coding +FE_FLOATING_POINT_EQUALITY.group = codingMinor +ICAST_IDIV_CAST_TO_DOUBLE.group = coding +ICAST_INTEGER_MULTIPLY_CAST_TO_LONG.group = coding +EQ_DOESNT_OVERRIDE_EQUALS.group = coding +NS_DANGEROUS_NON_SHORT_CIRCUIT.group = codingMinor +DLS_DEAD_LOCAL_STORE_IN_RETURN.group = codingMinor +INT_BAD_REM_BY_1.group = codingMinor + +DLS_DEAD_LOCAL_STORE.group = codingWarning +RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT.group = codingWarning +ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD.group = codingWarning +UC_USELESS_OBJECT.group = codingWarning +SF_SWITCH_NO_DEFAULT.group = codingWarning +UC_USELESS_CONDITION.group = codingWarning +BC_UNCONFIRMED_CAST.group = codingWarning +RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE.group = codingWarning +BC_VACUOUS_INSTANCEOF.group = codingWarning +REC_CATCH_EXCEPTION.group = codingWarning +UC_USELESS_VOID_METHOD.group = codingWarning +SA_FIELD_DOUBLE_ASSIGNMENT.group = codingWarning +DB_DUPLICATE_BRANCHES.group = codingWarning +URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD.group = codingWarning +UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD.group = codingWarning +NP_LOAD_OF_KNOWN_NULL_VALUE.group = codingWarning +DMI_HARDCODED_ABSOLUTE_FILENAME.group = codingWarning +SF_SWITCH_FALLTHROUGH.group = codingWarning +RV_RETURN_VALUE_IGNORED_INFERRED.group = codingWarning +IM_AVERAGE_COMPUTATION_COULD_OVERFLOW.group = codingWarning +NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE.group = codingWarning +UCF_USELESS_CONTROL_FLOW.group = codingWarning +SA_LOCAL_SELF_ASSIGNMENT.group = codingWarning +INT_VACUOUS_BIT_OPERATION.group = codingWarning +RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE.group = codingWarning +NS_NON_SHORT_CIRCUIT.group = codingWarning +UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD.group = codingWarning +NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD.group = codingWarning +QF_QUESTIONABLE_FOR_LOOP.group = codingWarning +SA_LOCAL_DOUBLE_ASSIGNMENT.group = codingWarning +NP_IMMEDIATE_DEREFERENCE_OF_READLINE.group = codingWarning +ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT.group = codingWarning +NP_DEREFERENCE_OF_READLINE_VALUE.group = codingWarning +EQ_UNUSUAL.group = codingWarning +NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE.group = codingWarning +DLS_DEAD_LOCAL_STORE_OF_NULL.group = codingWarning +UCF_USELESS_CONTROL_FLOW_NEXT_LINE.group = codingWarning +IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD.group = codingWarning +DMI_NONSERIALIZABLE_OBJECT_WRITTEN.group = codingWarning +RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE.group = codingWarning +RV_DONT_JUST_NULL_CHECK_READLINE.group = codingWarning +RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES.group = codingWarning +UC_USELESS_OBJECT_STACK.group = codingWarning diff --git a/src/perllib/Web_CAT/ErrorMapper.pm b/src/perllib/Web_CAT/ErrorMapper.pm index cc86784..9715b12 100644 --- a/src/perllib/Web_CAT/ErrorMapper.pm +++ b/src/perllib/Web_CAT/ErrorMapper.pm @@ -19,6 +19,8 @@ use Exporter qw(import); compilerErrorEnhancedMessage setResultDir codingStyleMessageValue + findBugsMessage + enhancedFindBugsMessage ); # Compiler Errors: "Error Message" is the key and the anchor tag from below url @@ -410,11 +412,55 @@ my @regularExpressionErrorMessages = ( # Short Error Messages for Errors from Static Analysis Tools. # Key is the Checkstyle or pmd error Id. my %codingStyleShortMessages = ( - 'JUnit3TestsHaveAssertions' => "Test method contains no assertions", - 'JUnit4TestsHaveAssertions' => "Test method contains no assertions", + 'JUnit3TestsHaveAssertions' => "JUnit test methods must call at least one assertion method.", + 'JUnit4TestsHaveAssertions' => "JUnit test methods must call at least one assertion method.", 'JavadocMethod' => 'Javadoc issue' ); +my %findbugsMessages = ( + 'UC_USELESS_CONDITION' => "This condition always gives the same result. The value of the variable may have been narrowed to always match the condition in a previous statement or you may be using the = operator instead of == by mistake. Update the condition or remove it, as it is not necessary.", + 'ES_COMPARING_STRINGS_WITH_EQ' => "

The == operator compares the memory addresses of two Strings. Use equals() instead.

", + 'DLS_DEAD_LOCAL_STORE' => "This local variable has a value, but the value was never used. Make sure the value is used where intended, or remove it if it is not needed.", + 'ICAST_IDIV_CAST_TO_DOUBLE' => "When using integers in a division statement, Java will automatically use integer division. This results in truncating the result where the decimal part is removed. Instead of using, for example, 2, use 2.0 on the bottom of the division.", + 'RV_RETURN_VALUE_IGNORED' => "Immutable objects like Strings are not updated when a method is called on them. If you want the updated value, you will have to assign the result of the method to a variable.", + 'RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT' => "Immutable objects like Strings are not updated when a method is called on them. If you want the updated value, you will have to assign the result of the method to a variable.", + 'SA_LOCAL_SELF_ASSIGNMENT' => "This variable has been assigned to itself with no modification to its value. Unless the value of a variable needs to be changed, it does not need to be reassigned.", + 'IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN' => "A parameter of this method was overwritten without using its value in the method. Create a new variable instead of assigning over the parameter value.", + 'QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT' => "A single = means assignment, and a double == means comparison. It appears that the wrong one is used as a condition.", + 'ES_COMPARING_PARAMETER_STRING_WITH_EQ' => "The == compares the memory addresses of two Strings. Consider using equals() instead. One of the String being compared is a parameter.", + 'GC_UNRELATED_TYPES' => "A collection has a specific type of objects it contains. Make sure the item you are trying to add to the collection has the same type as the rest of the collection.", + 'DLS_DEAD_LOCAL_STORE_IN_RETURN' => "Placing a = operator in a return statement has no effect. Place any assignments before the return. If you want a boolean comparison in the return instead, use ==.", + 'RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE' => "This variable has already been dereferenced somewhere in the code. If the code got to this point, it means the value was non-null at the time and cannot be null here unless it was modified. Make sure the earlier dereference was appropriate or remove this null check.", + 'UC_USELESS_OBJECT' => "This object is created using the new operator and possibly modified, but it does not do anything in this method or in a different method. Make sure this object is used; otherwise, it can be removed.", + 'IL_INFINITE_LOOP' => "This loop does not have a way to terminate. Check the condition in while(...) to make sure the code will ever not match the condition.", + 'EC_NULL_ARG' => "Comparing any Object to null will always return false. This comparison is not necessary. Make sure you have initialized all your Objects.", + 'UCF_USELESS_CONTROL_FLOW' => "This control flow statement (if, else if, else) does not affect the flow of the program. The same thing will happen whether the statement is executed or not. Remove the statement if it is not needed or add additional logic within the body of the statement.", + 'IL_INFINITE_RECURSIVE_LOOP' => "This recursive loop does not terminate. Check that a base case is included and that the recursion will eventually reach that base case.", + 'RANGE_STRING_INDEX' => "String method is called and specified string index is out of bounds. This will result in a StringIndexOutOfBoundsException at runtime.", + 'RpC_REPEATED_CONDITIONAL_TEST' => "The same conditional statement is used twice. Remove the repeated condition or update it to reflect actual intent.", + 'NP_ALWAYS_NULL' => "A null pointer is dereferenced here. This will lead to a NullPointerException when the code is executed. Check that all Objects are initialized correctly.", + 'DB_DUPLICATE_BRANCHES' => "This method uses the same code to implement two branches of a conditional statement. Only one branch should be used. Check to ensure that this isn't a coding mistake.", + 'EC_BAD_ARRAY_COMPARE' => "The equals() method compares the memory addresses of arrays instead of the contents of the arrays. Consider using Arrays.equals() or write your own loop comparison.", + 'EC_UNRELATED_TYPES' => "This equals() comparison is comparing Objects of two different class types. Double check the arguments to equals().", + 'QF_QUESTIONABLE_FOR_LOOP' => "Make sure this loop is checking against the variable that you are incrementing.", + 'UCF_USELESS_CONTROL_FLOW_NEXT_LINE' => "This control flow statement (if, else if, else) does not affect the flow of the program. The same thing will happen whether the statement is executed or not. Be sure that a control statement has brackets after {} instead of a semicolon.", + 'DLS_DEAD_LOCAL_INCREMENT_IN_RETURN' => "The ++ operator happens after the method has already returned. When returning, be sure to increment before returning the value.", + 'SA_LOCAL_SELF_COMPARISON' => "This method compares a local variable with itself, and may indicate a typo or a logic error. Make sure that you are comparing the right things.", + 'ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND' => "An integer value is being passed to Math.round(). It does not make sense to round an integer because it is already a whole number. Check to see if a double or float was intended.", + 'RANGE_ARRAY_INDEX' => "An array operation is performed, but the array index is out of bounds, which will result in ArrayIndexOutOfBoundsException at runtime.", + 'NP_NULL_ON_SOME_PATH' => "There is a branch of statement that, if executed, guarantees that a null value will be dereferenced, which would generate a NullPointerException when the code is executed.", + 'DLS_OVERWRITTEN_INCREMENT' => "The ++ operator happens after assignment if used after a variable. Be sure to increment before assigning a variable to itself.", + 'SA_LOCAL_DOUBLE_ASSIGNMENT' => "The = operator only needs to be used once per statement. You likely have a typo if it is included more than once.", + 'NS_NON_SHORT_CIRCUIT' => "In Java, && and || are the short-circuit operators for logic. You want to use these instead of & and | because they will stop checking statements once one of them is false.", + 'DMI_INVOKING_TOSTRING_ON_ARRAY' => "Using toString() on an array prints out its memory location, not its contents. Try using Arrays.toString().", + 'ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD' => "An object typically represents an instance of a class. For example, a Car can have more than one Tire. If you write to a static field from one of the Tires, it will update the value for all of them. When you have multiples of an object, avoid updating static fields.", + 'DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES' => "A collection cannot contain itself. Double check you are adding the right thing to your collection.", + 'INT_BAD_REM_BY_1' => "Modular arithmetic (%) gives the remainder of a division operation. Anything divided by 1 has a remainder of 0, so x % 1 == 0.", + 'NS_DANGEROUS_NON_SHORT_CIRCUIT' => "Using only one | or & means that conditional statements will not short circuit. Even if the first condition fails, the second will be evaluated. The second condition may cause an exception or other side effects.", + 'EC_UNRELATED_CLASS_AND_INTERFACE' => "An interface cannot be compared to an object that does not implement that interface. Make sure the right two objects are being compared and that they are of the same class.", + 'VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED' => "A format-string method with a variable number of arguments is called, but more arguments are passed than are actually used by the format string. This won't cause a runtime exception, but the code may be silently omitting information that was intended to be included in the formatted string.", + 'SA_LOCAL_SELF_COMPUTATION' => "This method performs a nonsensical computation of a local variable with another reference to the same variable (e.g., x & x or x - x). Because of the nature of the computation, this operation doesn't seem to make sense, and may indicate a typo or a logic error. Double check the computation.", +); my $resultDir; @@ -432,6 +478,42 @@ sub codingStyleMessageValue return ''; } + +sub findBugsMessage +{ + my $rule = shift; + my $msg = ''; + + if (defined $findbugsMessages{$rule}) + { + $msg = $findbugsMessages{$rule}; + if ($msg !~ m/^\s*<[a-zA-Z][^>]*>/so) + { + $msg = '

' . htmlEscape($msg) . '

'; + } + } + + return $msg; +} + + +sub enhancedFindBugsMessage +{ + my $rule = shift; + my $msg = shift; + +# if (defined $findbugsMessages{$rule}) +# { +# my $enhanced = $findbugsMessages{$rule}; +# if () +# { +# $enhanced = '

' +# } +# } + + return ''; +} + sub setResultDir { $resultDir = shift; diff --git a/src/perllib/Web_CAT/ExpandedFeedbackUtil.pm b/src/perllib/Web_CAT/ExpandedFeedbackUtil.pm index 7a6d416..0fe0a48 100644 --- a/src/perllib/Web_CAT/ExpandedFeedbackUtil.pm +++ b/src/perllib/Web_CAT/ExpandedFeedbackUtil.pm @@ -38,6 +38,10 @@ sub extractAboveBelowLinesOfCode my $filePath = shift; my $errorLineNum = shift; + carp "no error line provided" + if !defined($errorLineNum) || $errorLineNum eq ''; + + if (!defined $filePath) { # cluck, but on stdout diff --git a/src/pit/README.md b/src/pit/README.md new file mode 100644 index 0000000..e1e7f45 --- /dev/null +++ b/src/pit/README.md @@ -0,0 +1,9 @@ +# VT Pit Selective Mutators JARs: + +This is the Pit version which uses the Selective Mutator Operators as part of the research conducted by the Digital Education Lab in Virginia Tech. +The Selective Mutator Operators are by default: +- AOD (Arithmetic Operator Deletion) +- RemoveConditionals + +The folder "required JARs for IDE plugin" contains the three necessary JARs required for modified PitClipse IDE plugin. + diff --git a/src/plugin-rulesets/design.xml b/src/plugin-rulesets/design.xml index 1f356f8..4962342 100644 --- a/src/plugin-rulesets/design.xml +++ b/src/plugin-rulesets/design.xml @@ -69,9 +69,10 @@ - + diff --git a/src/plugin-rulesets/unusedcode.xml b/src/plugin-rulesets/unusedcode.xml index db08e16..f39016f 100644 --- a/src/plugin-rulesets/unusedcode.xml +++ b/src/plugin-rulesets/unusedcode.xml @@ -13,9 +13,9 @@ - + to be unnecessary."/ --> diff --git a/src/plugin-rulesets/vt.xml b/src/plugin-rulesets/vt.xml index c36b915..7454f23 100644 --- a/src/plugin-rulesets/vt.xml +++ b/src/plugin-rulesets/vt.xml @@ -218,6 +218,44 @@ + + + All assertThat() assertions in JUnit test methods + should contain one or more chained method calls defining the + expected behavior, such as .isEqualTo(), .isTrue(), isFalse(), etc. + + + + + + + + + 2 + + + + + +