From 4880ca62d30699639cc82952972e6e8a71a7ec92 Mon Sep 17 00:00:00 2001 From: Geoffrey Challen Date: Mon, 5 Sep 2022 17:02:37 -0500 Subject: [PATCH] Finish STDIN support and test. --- build.gradle.kts | 2 +- lib/build.gradle.kts | 8 +- lib/src/main/kotlin/QuestionHelpers.kt | 36 ++- lib/src/main/kotlin/TestQuestion.kt | 2 +- lib/src/test/kotlin/TestValidation.kt | 8 + lib/src/test/resources/questions.json | 240 +++++++++++++++++- plugin/build.gradle.kts | 8 +- plugin/src/main/kotlin/save/SaveQuestions.kt | 4 +- server/build.gradle.kts | 10 +- server/src/main/kotlin/Main.kt | 127 ++++----- ...llinois.cs.cs125.questioner.server.version | 2 +- 11 files changed, 358 insertions(+), 89 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3cc0976..79dcf9f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } subprojects { group = "com.github.cs125-illinois.questioner" - version = "2022.8.0" + version = "2022.9.0" tasks.withType { kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 0328cca..e14fb92 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -12,11 +12,11 @@ dependencies { implementation("org.ow2.asm:asm:9.3") api("com.beyondgrader.resource-agent:agent:2022.6.6") - api("com.github.cs125-illinois.jeed:core:2022.8.0") - api("com.github.cs125-illinois:jenisol:2022.8.0") - api("io.kotest:kotest-runner-junit5:5.4.1") + api("com.github.cs125-illinois.jeed:core:2022.9.0") + api("com.github.cs125-illinois:jenisol:2022.9.0") + api("io.kotest:kotest-runner-junit5:5.4.2") api("com.google.truth:truth:1.1.3") - api("com.github.cs125-illinois:libcs1:2022.7.0") + api("com.github.cs125-illinois:libcs1:2022.9.0") } tasks { val sourcesJar by creating(Jar::class) { diff --git a/lib/src/main/kotlin/QuestionHelpers.kt b/lib/src/main/kotlin/QuestionHelpers.kt index 1d78316..81c1b3c 100644 --- a/lib/src/main/kotlin/QuestionHelpers.kt +++ b/lib/src/main/kotlin/QuestionHelpers.kt @@ -394,23 +394,43 @@ class InvertingClassLoader( } } -class ResettingInputStream : InputStream() { - private var currentInputStream: ByteArrayInputStream = ByteArrayInputStream("".toByteArray()) - set(value) { - currentInputStream.close() - field = value +class BumpingInputStream : InputStream() { + private var stream = ByteArrayInputStream("".toByteArray()) + private lateinit var inputs: List + + private var index = 0 + private var usedIndex = false + + fun setInputs(_inputs: List) { + index = 0 + usedIndex = false + inputs = _inputs + stream = ByteArrayInputStream(inputs.getOrNull(index) ?: "".toByteArray()) + } + + fun bump() { + if (usedIndex) { + index++ + stream = ByteArrayInputStream(inputs.getOrNull(index) ?: "".toByteArray()) + usedIndex = false } + } override fun read(): Int { - return currentInputStream.read() + usedIndex = true + return stream.read() } } -fun bindJeedCaptureOutputControlInput(stdinStream: ResettingInputStream): CaptureOutputControlInput { +fun bindJeedCaptureOutputControlInput(stdinStream: BumpingInputStream): CaptureOutputControlInput { return fun(stdin: List, run: () -> Any?): CapturedResult { + stdinStream.setInputs(stdin.map { "$it\n".toByteArray() }) + val outputListener = object : Sandbox.OutputListener { + override fun stdout(int: Int) { + stdinStream.bump() + } override fun stderr(int: Int) {} - override fun stdout(int: Int) {} } var resourceUsage: ResourceMonitoringCheckpoint? = null val jeedOutput = Sandbox.redirectOutput(outputListener) { diff --git a/lib/src/main/kotlin/TestQuestion.kt b/lib/src/main/kotlin/TestQuestion.kt index 49c6b73..a28cb6c 100644 --- a/lib/src/main/kotlin/TestQuestion.kt +++ b/lib/src/main/kotlin/TestQuestion.kt @@ -110,7 +110,7 @@ suspend fun Question.test( minTestCount = settings.minTestCount, maxTestCount = settings.maxTestCount ) - val systemInStream = ResettingInputStream() + val systemInStream = BumpingInputStream() val executionArguments = Sandbox.ExecutionArguments( timeout = settings.timeout.toLong(), classLoaderConfiguration = classLoaderConfiguration, diff --git a/lib/src/test/kotlin/TestValidation.kt b/lib/src/test/kotlin/TestValidation.kt index dbc8c9c..4656ab2 100644 --- a/lib/src/test/kotlin/TestValidation.kt +++ b/lib/src/test/kotlin/TestValidation.kt @@ -48,4 +48,12 @@ class TestValidation : StringSpec({ report!!.requiredTestCount shouldBeGreaterThan 0 } } + "stdin interleaving should work" { + validator.validate("Input Interleaving Test", force = true, testing = true) + .also { (question, report) -> + question.validated shouldBe true + report shouldNotBe null + report!!.requiredTestCount shouldBeGreaterThan 0 + } + } }) \ No newline at end of file diff --git a/lib/src/test/resources/questions.json b/lib/src/test/resources/questions.json index a57988b..d4d2628 100644 --- a/lib/src/test/resources/questions.json +++ b/lib/src/test/resources/questions.json @@ -1965,12 +1965,244 @@ }, "fauxStatic": false }, + "Input Interleaving Test": { + "name": "Input Interleaving Test", + "type": "KLASS", + "klass": "Question", + "metadata": { + "contentHash": "07f28386753a725afce9dc22bdad83f0", + "packageName": "com.examples.inputinterleavingtest", + "version": "2022.9.0", + "author": "challen@illinois.edu", + "javaDescription": "

Testing STDIN generation and interleaving.

", + "kotlinDescription": "

Testing STDIN generation and interleaving. In Kotlin!

", + "usedFiles": [ + "/Users/challen/code/questioner-problems/src/main/java/com/examples/inputinterleavingtest/correct/kotlin/Question.kt" + ], + "focused": false + }, + "annotatedControls": {}, + "question": { + "klass": "Question", + "contents": "import java.util.Arrays;\nimport java.util.Random;\nimport java.util.Scanner;\nimport edu.illinois.cs.cs125.jenisol.core.EdgeType;\nimport edu.illinois.cs.cs125.jenisol.core.Limit;\nimport edu.illinois.cs.cs125.jenisol.core.ProvideSystemIn;\nimport edu.illinois.cs.cs125.jenisol.core.RandomType;\nimport edu.illinois.cs.cs125.jenisol.core.SimpleType;\nimport edu.illinois.cs.cs125.jenisol.core.generators.SystemIn;\nimport edu.illinois.cs.cs125.questioner.lib.Correct;\n\n/*\n * Testing STDIN generation and interleaving.\n */\n\n@SuppressWarnings(\"StringOperationCanBeSimplified\")\n@Correct(name = \"Input Interleaving Test\", version = \"2022.9.0\", author = \"challen@illinois.edu\")\npublic class Question {\n @ProvideSystemIn\n @Limit(1)\n public static void echo() {\n System.out.println(\"Hello!\");\n Scanner scanner = new Scanner(System.in);\n String first = scanner.nextLine();\n System.out.println(\"First: \" + first);\n String second = scanner.nextLine();\n System.out.println(\"Second: \" + second);\n }\n\n @SimpleType\n private static final SystemIn[] SIMPLE =\n new SystemIn[] {new SystemIn(Arrays.asList(\"Test\", \"Me\"))};\n\n @EdgeType private static final SystemIn[] EDGE = new SystemIn[] {};\n\n @RandomType\n private static SystemIn randomInput(Random random) {\n return new SystemIn(\n Arrays.asList(new String(\"\" + random.nextInt()), new String(\"\" + random.nextInt())));\n }\n}", + "language": "java", + "path": "/Users/challen/code/questioner-problems/src/main/java/com/examples/inputinterleavingtest/Question.java" + }, + "correct": { + "klass": "Question", + "contents": "import java.util.Scanner;\n\npublic class Question {\n public static void echo() {\n System.out.println(\"Hello!\");\n Scanner scanner = new Scanner(System.in);\n String first = scanner.nextLine();\n System.out.println(\"First: \" + first);\n String second = scanner.nextLine();\n System.out.println(\"Second: \" + second);\n }\n}\n", + "language": "java", + "path": "/Users/challen/code/questioner-problems/src/main/java/com/examples/inputinterleavingtest/Question.java", + "complexity": 1, + "features": { + "featureMap": { + "LOCAL_VARIABLE_DECLARATIONS": 3, + "VARIABLE_ASSIGNMENTS": 3, + "ARITHMETIC_OPERATORS": 2, + "METHOD": 1, + "STRING": 2, + "CLASS": 1, + "NEW_KEYWORD": 1, + "VISIBILITY_MODIFIERS": 2, + "STATIC_METHOD": 1, + "IMPORT": 1, + "PRINT_STATEMENTS": 3, + "DOT_NOTATION": 3, + "DOTTED_METHOD_CALL": 2, + "DOTTED_VARIABLE_ACCESS": 1 + }, + "importList": [ + "java.util.Scanner" + ], + "typeList": [ + "Scanner", + "String" + ], + "identifierList": [ + "scanner", + "first", + "second" + ], + "dottedMethodList": [ + "println", + "nextLine" + ] + }, + "lineCount": { + "source": 11, + "comment": 0, + "blank": 2 + }, + "expectedDeadCount": 1 + }, + "alternativeSolutions": [ + { + "klass": "QuestionKt", + "contents": "import java.util.Scanner\n\n\nfun echo() {\n println(\"Hello!\")\n val scanner = Scanner(System.`in`)\n val first = scanner.nextLine()\n println(\"First: $first\")\n val second = scanner.nextLine()\n println(\"Second: $second\")\n}", + "language": "kotlin", + "path": "/Users/challen/code/questioner-problems/src/main/java/com/examples/inputinterleavingtest/correct/kotlin/Question.kt", + "complexity": 1, + "features": { + "featureMap": { + "LOCAL_VARIABLE_DECLARATIONS": 3, + "VARIABLE_ASSIGNMENTS": 3, + "METHOD": 1, + "IMPORT": 1, + "PRINT_STATEMENTS": 3, + "DOT_NOTATION": 3, + "DOTTED_METHOD_CALL": 2, + "DOTTED_VARIABLE_ACCESS": 1 + }, + "importList": [ + "java.util.Scanner" + ], + "typeList": [], + "identifierList": [], + "dottedMethodList": [ + "nextLine" + ] + }, + "lineCount": { + "source": 9, + "comment": 0, + "blank": 2 + } + } + ], + "incorrect": [], + "common": [], + "importWhitelist": [], + "importBlacklist": [], + "slug": "input-interleaving-test", + "kotlinSolution": { + "klass": "QuestionKt", + "contents": "import java.util.Scanner\n\n\nfun echo() {\n println(\"Hello!\")\n val scanner = Scanner(System.`in`)\n val first = scanner.nextLine()\n println(\"First: $first\")\n val second = scanner.nextLine()\n println(\"Second: $second\")\n}", + "language": "kotlin", + "path": "/Users/challen/code/questioner-problems/src/main/java/com/examples/inputinterleavingtest/correct/kotlin/Question.kt", + "complexity": 1, + "features": { + "featureMap": { + "LOCAL_VARIABLE_DECLARATIONS": 3, + "VARIABLE_ASSIGNMENTS": 3, + "METHOD": 1, + "IMPORT": 1, + "PRINT_STATEMENTS": 3, + "DOT_NOTATION": 3, + "DOTTED_METHOD_CALL": 2, + "DOTTED_VARIABLE_ACCESS": 1 + }, + "importList": [ + "java.util.Scanner" + ], + "typeList": [], + "identifierList": [], + "dottedMethodList": [ + "nextLine" + ] + }, + "lineCount": { + "source": 9, + "comment": 0, + "blank": 2 + } + }, + "hasKotlin": true, + "published": { + "name": "Input Interleaving Test", + "type": "KLASS", + "path": "input-interleaving-test", + "author": "challen@illinois.edu", + "version": "2022.9.0", + "packageName": "com.examples.inputinterleavingtest", + "languages": [ + "java", + "kotlin" + ], + "descriptions": { + "java": "

Testing STDIN generation and interleaving.

", + "kotlin": "

Testing STDIN generation and interleaving. In Kotlin!

" + }, + "complexity": { + "java": 1, + "kotlin": 1 + }, + "features": { + "java": { + "featureMap": { + "LOCAL_VARIABLE_DECLARATIONS": 3, + "VARIABLE_ASSIGNMENTS": 3, + "ARITHMETIC_OPERATORS": 2, + "METHOD": 1, + "STRING": 2, + "CLASS": 1, + "NEW_KEYWORD": 1, + "VISIBILITY_MODIFIERS": 2, + "STATIC_METHOD": 1, + "IMPORT": 1, + "PRINT_STATEMENTS": 3, + "DOT_NOTATION": 3, + "DOTTED_METHOD_CALL": 2, + "DOTTED_VARIABLE_ACCESS": 1 + }, + "importList": [ + "java.util.Scanner" + ], + "typeList": [ + "Scanner", + "String" + ], + "identifierList": [ + "scanner", + "first", + "second" + ], + "dottedMethodList": [ + "println", + "nextLine" + ] + }, + "kotlin": { + "featureMap": { + "LOCAL_VARIABLE_DECLARATIONS": 3, + "VARIABLE_ASSIGNMENTS": 3, + "METHOD": 1, + "IMPORT": 1, + "PRINT_STATEMENTS": 3, + "DOT_NOTATION": 3, + "DOTTED_METHOD_CALL": 2, + "DOTTED_VARIABLE_ACCESS": 1 + }, + "importList": [ + "java.util.Scanner" + ], + "typeList": [], + "identifierList": [], + "dottedMethodList": [ + "nextLine" + ] + } + }, + "lineCounts": { + "java": { + "source": 11, + "comment": 0, + "blank": 2 + }, + "kotlin": { + "source": 9, + "comment": 0, + "blank": 2 + } + } + }, + "fauxStatic": false + }, "With Imports": { "name": "With Imports", "type": "KLASS", "klass": "Question", "metadata": { - "contentHash": "cb19a8122edb12c3937dd28e4820ca15", + "contentHash": "28944ac134c1b61f898a4d9596831bb7", "packageName": "com.examples.withimports", "version": "2022.7.0", "author": "challen@illinois.edu", @@ -1984,7 +2216,7 @@ "annotatedControls": {}, "question": { "klass": "Question", - "contents": "import edu.illinois.cs.cs125.questioner.lib.Correct;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/*\n * Example to test use of import statements.\n */\n\n@Correct(name = \"With Imports\", version = \"2022.7.0\", author = \"challen@illinois.edu\")\npublic class Question {\n public static List max(int first, int second) {\n return Arrays.asList(first, second);\n }\n}", + "contents": "import edu.illinois.cs.cs125.questioner.lib.Correct;\nimport java.util.Arrays;\nimport java.util.List;\n\n/*\n * Example to test use of import statements.\n */\n\n@Correct(name = \"With Imports\", version = \"2022.7.0\", author = \"challen@illinois.edu\")\npublic class Question {\n public static List max(int first, int second) {\n return Arrays.asList(first, second);\n }\n}", "language": "java", "path": "/Users/challen/code/questioner-problems/src/main/java/com/examples/withimports/Question.java" }, @@ -2612,7 +2844,7 @@ "type": "METHOD", "klass": "Question", "metadata": { - "contentHash": "7265340f9d922ca3dfe7ff222f5a309d", + "contentHash": "f53448151d802ca1846e2f5c3475f44d", "packageName": "com.examples.withfeaturecheck", "version": "2022.7.0", "author": "challen@illinois.edu", @@ -2628,7 +2860,7 @@ "annotatedControls": {}, "question": { "klass": "Question", - "contents": "import edu.illinois.cs.cs125.jeed.core.Features;\nimport edu.illinois.cs.cs125.jenisol.core.BoundComplexity;\nimport edu.illinois.cs.cs125.jenisol.core.FilterParameters;\nimport edu.illinois.cs.cs125.jenisol.core.SkipTest;\nimport edu.illinois.cs.cs125.questioner.lib.CheckFeatures;\nimport edu.illinois.cs.cs125.questioner.lib.Correct;\nimport edu.illinois.cs.cs125.questioner.lib.FeatureCheckException;\nimport edu.illinois.cs.cs125.questioner.lib.Wrap;\nimport edu.illinois.cs.cs125.questioner.lib.features.FeatureHelpers;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/*\n * Test whether feature checking works.\n */\n\n@Correct(name = \"With Feature Check\", author = \"challen@illinois.edu\", version = \"2022.7.0\")\n@Wrap\npublic class Question {\n @FilterParameters\n private static void filterParameters(int range) {\n if (range < 0) {\n throw new SkipTest();\n }\n if (range > 1024) {\n throw new BoundComplexity();\n }\n }\n\n int sumTo(int range) {\n return (range * (range + 1)) / 2;\n }\n\n @CheckFeatures\n private static List checkFeatures(Features solution, Features submission) {\n if (FeatureHelpers.usesLoop(submission)) {\n throw new FeatureCheckException(\"Submission uses a loop\");\n }\n return new ArrayList<>();\n }\n}", + "contents": "import edu.illinois.cs.cs125.jeed.core.Features;\nimport edu.illinois.cs.cs125.jenisol.core.BoundComplexity;\nimport edu.illinois.cs.cs125.jenisol.core.FilterParameters;\nimport edu.illinois.cs.cs125.jenisol.core.SkipTest;\nimport edu.illinois.cs.cs125.questioner.lib.CheckFeatures;\nimport edu.illinois.cs.cs125.questioner.lib.Correct;\nimport edu.illinois.cs.cs125.questioner.lib.FeatureCheckException;\nimport edu.illinois.cs.cs125.questioner.lib.Wrap;\nimport edu.illinois.cs.cs125.questioner.lib.features.FeatureHelpers;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/*\n * Test whether feature checking works.\n */\n\n@Correct(name = \"With Feature Check\", author = \"challen@illinois.edu\", version = \"2022.7.0\")\n@Wrap\npublic class Question {\n @FilterParameters\n private static void filterParameters(int range) {\n if (range < 0) {\n throw new SkipTest();\n }\n if (range > 1024) {\n throw new BoundComplexity();\n }\n }\n\n int sumTo(int range) {\n return (range * (range + 1)) / 2;\n }\n\n @CheckFeatures\n private static List checkFeatures(Features solution, Features submission) {\n if (FeatureHelpers.usesLoop(submission)) {\n throw new FeatureCheckException(\"Submission uses a loop\");\n }\n return new ArrayList<>();\n }\n}", "language": "java", "path": "/Users/challen/code/questioner-problems/src/main/java/com/examples/withfeaturecheck/Question.java" }, diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 9352cb8..48ee270 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -8,7 +8,7 @@ plugins { id("com.google.devtools.ksp") } dependencies { - antlr("org.antlr:antlr4:4.10.1") + antlr("org.antlr:antlr4:4.11.1") implementation("org.jetbrains.kotlin:kotlin-reflect:1.7.10") implementation(gradleApi()) @@ -20,12 +20,12 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") implementation("com.google.googlejavaformat:google-java-format:1.15.0") implementation("com.google.guava:guava:31.1-jre") - implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.3") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.4") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4") implementation("com.github.slugify:slugify:3.0.2") implementation("org.apache.httpcomponents.client5:httpclient5-fluent:5.1.3") - testImplementation("io.kotest:kotest-runner-junit5:5.4.1") + testImplementation("io.kotest:kotest-runner-junit5:5.4.2") } tasks.compileKotlin { dependsOn(tasks.generateGrammarSource) diff --git a/plugin/src/main/kotlin/save/SaveQuestions.kt b/plugin/src/main/kotlin/save/SaveQuestions.kt index cb1d931..cecdb89 100644 --- a/plugin/src/main/kotlin/save/SaveQuestions.kt +++ b/plugin/src/main/kotlin/save/SaveQuestions.kt @@ -13,6 +13,7 @@ import edu.illinois.cs.cs125.jenisol.core.FixedParameters import edu.illinois.cs.cs125.jenisol.core.InstanceValidator import edu.illinois.cs.cs125.jenisol.core.Limit import edu.illinois.cs.cs125.jenisol.core.NotNull +import edu.illinois.cs.cs125.jenisol.core.ProvideSystemIn import edu.illinois.cs.cs125.jenisol.core.RandomParameters import edu.illinois.cs.cs125.jenisol.core.RandomType import edu.illinois.cs.cs125.jenisol.core.SimpleType @@ -456,7 +457,8 @@ val annotationsToRemove = AlsoCorrect::class.java.simpleName, Configure::class.java.simpleName, Cite::class.java.simpleName, - Limit::class.java.simpleName + Limit::class.java.simpleName, + ProvideSystemIn::class.java.simpleName ) val annotationsToDestroy = setOf( diff --git a/server/build.gradle.kts b/server/build.gradle.kts index e76b511..06f0c38 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -18,14 +18,14 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect:1.7.10") implementation(project(":lib")) - implementation("io.ktor:ktor-server-netty:2.0.3") - implementation("io.ktor:ktor-server-content-negotiation:2.0.3") + implementation("io.ktor:ktor-server-netty:2.1.0") + implementation("io.ktor:ktor-server-content-negotiation:2.1.0") implementation("com.squareup.moshi:moshi-kotlin:1.13.0") - implementation("com.github.cs125-illinois:ktor-moshi:2022.4.0") + implementation("com.github.cs125-illinois:ktor-moshi:2022.9.0") implementation("org.mongodb:mongodb-driver:3.12.11") - implementation("org.slf4j:slf4j-api:1.7.36") - implementation("ch.qos.logback:logback-classic:1.2.11") + implementation("org.slf4j:slf4j-api:2.0.0") + implementation("ch.qos.logback:logback-classic:1.4.0") implementation("io.github.microutils:kotlin-logging:2.1.23") } task("createProperties") { diff --git a/server/src/main/kotlin/Main.kt b/server/src/main/kotlin/Main.kt index 5d90514..606fee4 100644 --- a/server/src/main/kotlin/Main.kt +++ b/server/src/main/kotlin/Main.kt @@ -15,21 +15,29 @@ import edu.illinois.cs.cs125.questioner.lib.ResourceMonitoring import edu.illinois.cs.cs125.questioner.lib.TestResults import edu.illinois.cs.cs125.questioner.lib.moshi.Adapters import edu.illinois.cs.cs125.questioner.lib.test -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.engine.* -import io.ktor.server.netty.* -import io.ktor.server.plugins.contentnegotiation.* -import io.ktor.server.request.* -import io.ktor.server.response.* -import io.ktor.server.routing.* -import kotlinx.coroutines.* +import io.ktor.http.HttpStatusCode +import io.ktor.server.application.Application +import io.ktor.server.application.call +import io.ktor.server.application.install +import io.ktor.server.engine.embeddedServer +import io.ktor.server.netty.Netty +import io.ktor.server.plugins.contentnegotiation.ContentNegotiation +import io.ktor.server.request.receive +import io.ktor.server.response.respond +import io.ktor.server.routing.get +import io.ktor.server.routing.post +import io.ktor.server.routing.routing +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import mu.KotlinLogging import org.bson.BsonDocument import java.security.SecureRandom import java.security.cert.X509Certificate import java.time.Instant -import java.util.* +import java.util.Properties import java.util.concurrent.Executors import javax.net.ssl.SSLContext import javax.net.ssl.X509TrustManager @@ -37,7 +45,6 @@ import kotlin.collections.forEach import kotlin.system.exitProcess import edu.illinois.cs.cs125.jeed.core.moshi.Adapters as JeedAdapters - private val moshi = Moshi.Builder().apply { JeedAdapters.forEach { add(it) } Adapters.forEach { add(it) } @@ -73,13 +80,13 @@ private val collection: MongoCollection = run { data class QuestionPath(val path: String, val version: String, val author: String) { companion object { fun fromSubmission(submission: Submission) = - QuestionPath(submission.path, submission.version!!, submission.author!!) + QuestionPath(submission.path, submission.version!!, submission.author!!) } } object Questions { private fun getQuestion(path: String) = collection.find( - Filters.and(Filters.eq("published.path", path), Filters.eq("latest", true)) + Filters.and(Filters.eq("published.path", path), Filters.eq("latest", true)) ).sort(Sorts.descending("updated")).let { @Suppress("ReplaceSizeZeroCheckWithIsEmpty") if (it.count() == 0) { @@ -95,12 +102,12 @@ object Questions { } private fun getQuestionByPath(path: QuestionPath) = collection.find( - Filters.and( - Filters.eq("published.path", path.path), - Filters.eq("published.version", path.version), - Filters.eq("published.author", path.author), - Filters.eq("latest", true) - ) + Filters.and( + Filters.eq("published.path", path.path), + Filters.eq("published.version", path.version), + Filters.eq("published.author", path.author), + Filters.eq("latest", true) + ) ).sort(Sorts.descending("updated")).let { @Suppress("ReplaceSizeZeroCheckWithIsEmpty") if (it.count() == 0) { @@ -134,15 +141,15 @@ object Questions { val start = Instant.now().toEpochMilli() val timeout = question.testingSettings!!.timeout * (System.getenv("TIMEOUT_MULTIPLIER")?.toInt() ?: 1) val settings = question.testingSettings!!.copy( - timeout = timeout, - disableLineCountLimit = submission.disableLineCountLimit, - disableAllocationLimit = submission.disableAllocationLimit + timeout = timeout, + disableLineCountLimit = submission.disableLineCountLimit, + disableAllocationLimit = submission.disableAllocationLimit ) logger.trace { "Testing ${question.name} with settings $settings" } return question.test( - submission.contents, - language = submission.language, - settings = settings + submission.contents, + language = submission.language, + settings = settings ).also { logger.trace { "Tested ${question.name} in ${Instant.now().toEpochMilli() - start}" } } @@ -151,24 +158,24 @@ object Questions { @JsonClass(generateAdapter = true) data class Submission( - val path: String, - val contents: String, - val language: Question.Language, - val disableLineCountLimit: Boolean = false, - val disableAllocationLimit: Boolean = true, // TODO: Switch to false when ready for allocation limiting - val version: String?, - val author: String? + val path: String, + val contents: String, + val language: Question.Language, + val disableLineCountLimit: Boolean = false, + val disableAllocationLimit: Boolean = true, // TODO: Switch to false when ready for allocation limiting + val version: String?, + val author: String? ) @JsonClass(generateAdapter = true) data class QuestionDescription( - val path: String, - val name: String, - val version: String, - val description: String, - val author: String, - val packageName: String, - val starter: String? + val path: String, + val name: String, + val version: String, + val description: String, + val author: String, + val packageName: String, + val starter: String? ) private val serverStarted = Instant.now() @@ -186,12 +193,12 @@ val versionString = run { @JsonClass(generateAdapter = true) data class Status( - val started: Instant = serverStarted, - val version: String = versionString + val started: Instant = serverStarted, + val version: String = versionString ) val threadPool = Executors.newFixedThreadPool(System.getenv("QUESTIONER_THREAD_POOL_SIZE")?.toIntOrNull() ?: 8) - .asCoroutineDispatcher() + .asCoroutineDispatcher() @JsonClass(generateAdapter = true) data class ServerResponse(val results: TestResults) @@ -240,15 +247,15 @@ fun Application.questioner() { val path = call.parameters["path"] ?: return@get call.respond(HttpStatusCode.BadRequest) val question = Questions.load(path) ?: return@get call.respond(HttpStatusCode.NotFound) call.respond( - QuestionDescription( - path, - question.name, - question.metadata.version, - question.metadata.javaDescription, - question.metadata.author, - question.metadata.packageName, - question.detemplatedJavaStarter - ) + QuestionDescription( + path, + question.name, + question.metadata.version, + question.metadata.javaDescription, + question.metadata.author, + question.metadata.packageName, + question.detemplatedJavaStarter + ) ) } get("/question/kotlin/{path}") { @@ -258,15 +265,15 @@ fun Application.questioner() { return@get call.respond(HttpStatusCode.NotFound) } call.respond( - QuestionDescription( - path, - question.name, - question.metadata.version, - question.metadata.kotlinDescription!!, - question.metadata.author, - question.metadata.packageName, - starter = question.detemplatedKotlinStarter - ) + QuestionDescription( + path, + question.name, + question.metadata.version, + question.metadata.kotlinDescription!!, + question.metadata.author, + question.metadata.packageName, + starter = question.detemplatedKotlinStarter + ) ) } } diff --git a/server/src/main/resources/edu.illinois.cs.cs125.questioner.server.version b/server/src/main/resources/edu.illinois.cs.cs125.questioner.server.version index 252a1d8..4aca86e 100644 --- a/server/src/main/resources/edu.illinois.cs.cs125.questioner.server.version +++ b/server/src/main/resources/edu.illinois.cs.cs125.questioner.server.version @@ -1 +1 @@ -version=2022.8.0 \ No newline at end of file +version=2022.9.0 \ No newline at end of file