From 1341bb045c0def9a9c1b70a090966a5b1d77b755 Mon Sep 17 00:00:00 2001 From: Geoffrey Challen Date: Tue, 28 Sep 2021 20:24:30 -0500 Subject: [PATCH] Fix bug, improve property errors on Kotlin. --- src/main/kotlin/Submission.kt | 75 +++++++++++++++++-- .../illinois/cs/cs125/jenisol/core/Helpers.kt | 9 ++- .../cs/cs125/jenisol/core/TestJavaExamples.kt | 9 +++ .../kotlindefaultparameter/Correct.java | 17 +++++ .../kotlindefaultparameter/Design0.java | 13 ++++ .../kotlindefaultparameter/Design1.kt | 3 + .../java/receiver/kotlinvalvar/Correct.java | 17 +++++ .../java/receiver/kotlinvalvar/Design0.kt | 3 + .../java/receiver/kotlinvalvar/Design1.kt | 4 + .../java/receiver/kotlinvarval/Correct.java | 13 ++++ .../java/receiver/kotlinvarval/Design0.kt | 3 + .../java/receiver/kotlinvarval/Design1.kt | 5 ++ 12 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 src/test/java/examples/java/receiver/kotlindefaultparameter/Correct.java create mode 100644 src/test/java/examples/java/receiver/kotlindefaultparameter/Design0.java create mode 100644 src/test/java/examples/java/receiver/kotlindefaultparameter/Design1.kt create mode 100644 src/test/java/examples/java/receiver/kotlinvalvar/Correct.java create mode 100644 src/test/java/examples/java/receiver/kotlinvalvar/Design0.kt create mode 100644 src/test/java/examples/java/receiver/kotlinvalvar/Design1.kt create mode 100644 src/test/java/examples/java/receiver/kotlinvarval/Correct.java create mode 100644 src/test/java/examples/java/receiver/kotlinvarval/Design0.kt create mode 100644 src/test/java/examples/java/receiver/kotlinvarval/Design1.kt diff --git a/src/main/kotlin/Submission.kt b/src/main/kotlin/Submission.kt index f802fb6..0634773 100644 --- a/src/main/kotlin/Submission.kt +++ b/src/main/kotlin/Submission.kt @@ -89,10 +89,39 @@ class Submission(val solution: Solution, val submission: Class<*>) { }?.let { executable -> executable.isAccessible = true solutionExecutable to executable - } ?: throw SubmissionDesignMissingMethodError( - submission, - solutionExecutable - ) + } ?: run { + @Suppress("ComplexCondition") + if (submission.isKotlin() && + solutionExecutable is Method && + ( + solutionExecutable.name.startsWith("get") || + solutionExecutable.name.startsWith("set") + ) + ) { + if (solutionExecutable.name.startsWith("get")) { + val field = solutionExecutable.name.removePrefix("get").let { + it[0].lowercaseChar() + it.substring(1) + } + throw SubmissionDesignKotlinNotAccessibleError( + submission, + field + ) + } else { + val field = solutionExecutable.name.removePrefix("set").let { + it[0].lowercaseChar() + it.substring(1) + } + throw SubmissionDesignKotlinNotModifiableError( + submission, + field + ) + } + } else { + throw SubmissionDesignMissingMethodError( + submission, + solutionExecutable + ) + } + } }.toMutableMap().also { if (solution.initializer != null) { it[solution.initializer] = solution.initializer @@ -119,7 +148,8 @@ class Submission(val solution: Solution, val submission: Class<*>) { return@forEach } if (executable is Constructor<*> && - executable.parameterTypes.last()?.name == "kotlin.jvm.internal.DefaultConstructorMarker" + executable.parameterTypes.lastOrNull()?.name == + "kotlin.jvm.internal.DefaultConstructorMarker" ) { return@forEach } @@ -130,6 +160,28 @@ class Submission(val solution: Solution, val submission: Class<*>) { return@forEach } } + @Suppress("ComplexCondition") + if (submission.isKotlin() && executable is Method && + (executable.name.startsWith("set") || executable.name.startsWith("get")) + ) { + if (executable.name.startsWith("set")) { + val field = executable.name.removePrefix("set").let { + it[0].lowercaseChar() + it.substring(1) + } + throw SubmissionDesignKotlinIsModifiableError( + submission, + field + ) + } else { + val field = executable.name.removePrefix("get").let { + it[0].lowercaseChar() + it.substring(1) + } + throw SubmissionDesignKotlinIsAccessibleError( + submission, + field + ) + } + } throw SubmissionDesignExtraMethodError( submission, executable @@ -468,6 +520,19 @@ class SubmissionDesignMissingMethodError(klass: Class<*>, executable: Executable } ${executable.fullName(klass.isKotlin())}" ) +class SubmissionDesignKotlinNotAccessibleError(klass: Class<*>, field: String) : SubmissionDesignError( + "Property $field on submission class ${klass.name} is not accessible (no getter is available)" +) +class SubmissionDesignKotlinNotModifiableError(klass: Class<*>, field: String) : SubmissionDesignError( + "Property $field on submission class ${klass.name} is not modifiable (no setter is available)" +) +class SubmissionDesignKotlinIsAccessibleError(klass: Class<*>, field: String) : SubmissionDesignError( + "Property $field on submission class ${klass.name} is accessible but should not be (getter is available)" +) +class SubmissionDesignKotlinIsModifiableError(klass: Class<*>, field: String) : SubmissionDesignError( + "Property $field on submission class ${klass.name} is modifiable but should not be (setter is available)" +) + class SubmissionDesignExtraMethodError(klass: Class<*>, executable: Executable) : SubmissionDesignError( "Submission class ${klass.name} provided extra ${ if (executable.isStatic() && !klass.isKotlin()) { diff --git a/src/test/java/edu/illinois/cs/cs125/jenisol/core/Helpers.kt b/src/test/java/edu/illinois/cs/cs125/jenisol/core/Helpers.kt index a414e1a..c6358ee 100644 --- a/src/test/java/edu/illinois/cs/cs125/jenisol/core/Helpers.kt +++ b/src/test/java/edu/illinois/cs/cs125/jenisol/core/Helpers.kt @@ -87,7 +87,14 @@ fun Class<*>.test() = this.testingClasses().apply { check(isNotEmpty()) { "No incorrect examples.java.examples for $testName" } }.forEach { incorrect -> if (incorrect.simpleName.startsWith("Design")) { - shouldThrow { submission(incorrect) } + shouldThrow { + try { + submission(incorrect) + } catch (e: Exception) { + e.printStackTrace() + throw e + } + } } else { check(!primarySolution.isDesignOnly()) { "Can't test Incorrect* examples when solution is design only" diff --git a/src/test/java/edu/illinois/cs/cs125/jenisol/core/TestJavaExamples.kt b/src/test/java/edu/illinois/cs/cs125/jenisol/core/TestJavaExamples.kt index db84cea..5a4e1ea 100644 --- a/src/test/java/edu/illinois/cs/cs125/jenisol/core/TestJavaExamples.kt +++ b/src/test/java/edu/illinois/cs/cs125/jenisol/core/TestJavaExamples.kt @@ -301,6 +301,15 @@ class TestJavaExamples : StringSpec( examples.java.noreceiver.setsum.Correct::class.java.also { "${it.testName()}" { it.test() } } + examples.java.receiver.kotlindefaultparameter.Correct::class.java.also { + "${it.testName()}" { it.test() } + } + examples.java.receiver.kotlinvalvar.Correct::class.java.also { + "${it.testName()}" { it.test() } + } + examples.java.receiver.kotlinvarval.Correct::class.java.also { + "${it.testName()}" { it.test() } + } examples.java.receiver.timeouttest.Correct::class.java.also { "${it.testName()}" { val runnable = object : Runnable { diff --git a/src/test/java/examples/java/receiver/kotlindefaultparameter/Correct.java b/src/test/java/examples/java/receiver/kotlindefaultparameter/Correct.java new file mode 100644 index 0000000..67b8b50 --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlindefaultparameter/Correct.java @@ -0,0 +1,17 @@ +package examples.java.receiver.kotlindefaultparameter; + +public class Correct { + private String value; + + public Correct(String setValue) { + value = setValue; + } + + public void setValue(String setValue) { + value = setValue; + } + + public String getValue() { + return value; + } +} diff --git a/src/test/java/examples/java/receiver/kotlindefaultparameter/Design0.java b/src/test/java/examples/java/receiver/kotlindefaultparameter/Design0.java new file mode 100644 index 0000000..053a863 --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlindefaultparameter/Design0.java @@ -0,0 +1,13 @@ +package examples.java.receiver.kotlindefaultparameter; + +public class Design0 { + private String value; + + public Design0(String setValue) { + value = setValue; + } + + public void setValue(String setValue) { + value = setValue; + } +} diff --git a/src/test/java/examples/java/receiver/kotlindefaultparameter/Design1.kt b/src/test/java/examples/java/receiver/kotlindefaultparameter/Design1.kt new file mode 100644 index 0000000..6ece119 --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlindefaultparameter/Design1.kt @@ -0,0 +1,3 @@ +package examples.java.receiver.kotlindefaultparameter + +class Design1(var value: String = "") \ No newline at end of file diff --git a/src/test/java/examples/java/receiver/kotlinvalvar/Correct.java b/src/test/java/examples/java/receiver/kotlinvalvar/Correct.java new file mode 100644 index 0000000..b2f76db --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlinvalvar/Correct.java @@ -0,0 +1,17 @@ +package examples.java.receiver.kotlinvalvar; + +public class Correct { + private String value; + + public Correct(String setValue) { + value = setValue; + } + + public void setValue(String setValue) { + value = setValue; + } + + public String getValue() { + return value; + } +} diff --git a/src/test/java/examples/java/receiver/kotlinvalvar/Design0.kt b/src/test/java/examples/java/receiver/kotlinvalvar/Design0.kt new file mode 100644 index 0000000..33e95be --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlinvalvar/Design0.kt @@ -0,0 +1,3 @@ +package examples.java.receiver.kotlinvalvar + +class Design0(val value: String?) \ No newline at end of file diff --git a/src/test/java/examples/java/receiver/kotlinvalvar/Design1.kt b/src/test/java/examples/java/receiver/kotlinvalvar/Design1.kt new file mode 100644 index 0000000..33f47ec --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlinvalvar/Design1.kt @@ -0,0 +1,4 @@ +package examples.java.receiver.kotlinvalvar + +@Suppress("UnusedPrivateMember") +class Design1(private val value: String?) \ No newline at end of file diff --git a/src/test/java/examples/java/receiver/kotlinvarval/Correct.java b/src/test/java/examples/java/receiver/kotlinvarval/Correct.java new file mode 100644 index 0000000..54c0d62 --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlinvarval/Correct.java @@ -0,0 +1,13 @@ +package examples.java.receiver.kotlinvarval; + +public class Correct { + private String value; + + public Correct(String setValue) { + value = setValue; + } + + public String getValue() { + return value; + } +} diff --git a/src/test/java/examples/java/receiver/kotlinvarval/Design0.kt b/src/test/java/examples/java/receiver/kotlinvarval/Design0.kt new file mode 100644 index 0000000..513fb56 --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlinvarval/Design0.kt @@ -0,0 +1,3 @@ +package examples.java.receiver.kotlinvarval + +class Design0(var value: String?) \ No newline at end of file diff --git a/src/test/java/examples/java/receiver/kotlinvarval/Design1.kt b/src/test/java/examples/java/receiver/kotlinvarval/Design1.kt new file mode 100644 index 0000000..233e32a --- /dev/null +++ b/src/test/java/examples/java/receiver/kotlinvarval/Design1.kt @@ -0,0 +1,5 @@ +package examples.java.receiver.kotlinvarval + +class Design1(val value: String?) { + var whatever: Int = 0 +} \ No newline at end of file