diff --git a/containerrunner/src/main/resources/edu.illinois.cs.cs125.jeed.containerrunner.version b/containerrunner/src/main/resources/edu.illinois.cs.cs125.jeed.containerrunner.version index 95c1e3b8..25ee76e6 100644 --- a/containerrunner/src/main/resources/edu.illinois.cs.cs125.jeed.containerrunner.version +++ b/containerrunner/src/main/resources/edu.illinois.cs.cs125.jeed.containerrunner.version @@ -1 +1 @@ -version=2021.7.7 \ No newline at end of file +version=2021.7.8 \ No newline at end of file diff --git a/core/src/main/antlr/edu/illinois/cs/cs125/jeed/antlr/knippet/KnippetLexer.g4 b/core/src/main/antlr/edu/illinois/cs/cs125/jeed/antlr/knippet/KnippetLexer.g4 index 16cfada3..1b94186c 100644 --- a/core/src/main/antlr/edu/illinois/cs/cs125/jeed/antlr/knippet/KnippetLexer.g4 +++ b/core/src/main/antlr/edu/illinois/cs/cs125/jeed/antlr/knippet/KnippetLexer.g4 @@ -457,9 +457,9 @@ Inside_LongLiteral: LongLiteral -> type(LongLiteral) ; Inside_Identifier: Identifier -> type(Identifier) ; Inside_LabelReference: LabelReference -> type(LabelReference) ; Inside_LabelDefinition: LabelDefinition -> type(LabelDefinition) ; -Inside_Comment: (LineComment | DelimitedComment) -> channel(HIDDEN) ; -Inside_WS: WS -> skip ; -Inside_NL: NL -> skip ; +Inside_Comment: (LineComment | DelimitedComment) -> channel(1) ; +Inside_WS: WS -> channel(2) ; +Inside_NL: NL -> channel(2) ; mode LineString ; @@ -512,7 +512,7 @@ MultiLineStrExprStart : '${' -> pushMode(StringExpression) ; -MultiLineNL: NL -> skip ; +MultiLineNL: NL -> channel(2) ; mode StringExpression ; @@ -585,6 +585,6 @@ StrExpr_LongLiteral: LongLiteral -> type(LongLiteral) ; StrExpr_Identifier: Identifier -> type(Identifier) ; StrExpr_LabelReference: LabelReference -> type(LabelReference) ; StrExpr_LabelDefinition: LabelDefinition -> type(LabelDefinition) ; -StrExpr_Comment: (LineComment | DelimitedComment) -> channel(HIDDEN) ; -StrExpr_WS: WS -> skip ; -StrExpr_NL: NL -> skip ; +StrExpr_Comment: (LineComment | DelimitedComment) -> channel(1) ; +StrExpr_WS: WS -> channel(2) ; +StrExpr_NL: NL -> channel(2) ; diff --git a/core/src/main/kotlin/JavaMutation.kt b/core/src/main/kotlin/JavaMutation.kt index 9ec76938..71d8e15a 100644 --- a/core/src/main/kotlin/JavaMutation.kt +++ b/core/src/main/kotlin/JavaMutation.kt @@ -1,5 +1,6 @@ // ktlint-disable filename @file:Suppress("MatchingDeclarationName") + package edu.illinois.cs.cs125.jeed.core import edu.illinois.cs.cs125.jeed.core.antlr.JavaParser @@ -245,6 +246,18 @@ class JavaMutationListener(private val parsedSource: Source.ParsedSource) : Java ) ) } + if (contents == "+" || contents == "-") { + val text = parsedSource.contents(ctx.expression(1).toLocation()) + if (text == "1") { + mutations.add( + PlusOrMinusOneToZero( + ctx.expression(1).toLocation(), + parsedSource.contents(ctx.expression(1).toLocation()), + fileType + ) + ) + } + } } } @@ -348,6 +361,12 @@ class JavaMutationListener(private val parsedSource: Source.ParsedSource) : Java ctx.statementExpression?.also { mutations.add(RemoveStatement(ctx.toLocation(), parsedSource.contents(ctx.toLocation()), fileType)) } + ctx.BREAK()?.symbol?.also { + mutations.add(SwapBreakContinue(it.toLocation(), parsedSource.contents(it.toLocation()), fileType)) + } + ctx.CONTINUE()?.symbol?.also { + mutations.add(SwapBreakContinue(it.toLocation(), parsedSource.contents(it.toLocation()), fileType)) + } } init { diff --git a/core/src/main/kotlin/KotlinMutation.kt b/core/src/main/kotlin/KotlinMutation.kt index c4eb5885..5af571d2 100644 --- a/core/src/main/kotlin/KotlinMutation.kt +++ b/core/src/main/kotlin/KotlinMutation.kt @@ -65,6 +65,12 @@ class KotlinMutationListener(private val parsedSource: Source.ParsedSource) : Ko mutations.add(NullReturn(returnLocation, parsedSource.contents(returnLocation), fileType)) } } + ctx.BREAK()?.also { + mutations.add(SwapBreakContinue(ctx.toLocation(), parsedSource.contents(ctx.toLocation()), fileType)) + } + ctx.CONTINUE()?.also { + mutations.add(SwapBreakContinue(ctx.toLocation(), parsedSource.contents(ctx.toLocation()), fileType)) + } } override fun enterStatement(ctx: KotlinParser.StatementContext) { @@ -301,6 +307,16 @@ class KotlinMutationListener(private val parsedSource: Source.ParsedSource) : Ko val (frontLocation, backLocation) = ctx.locationPair() mutations.add(RemovePlus(frontLocation, parsedSource.contents(frontLocation), fileType)) mutations.add(RemovePlus(backLocation, parsedSource.contents(backLocation), fileType)) + val text = parsedSource.contents(ctx.multiplicativeExpression()[1].toLocation()) + if (text == "1") { + mutations.add( + PlusOrMinusOneToZero( + ctx.multiplicativeExpression()[1].toLocation(), + parsedSource.contents(ctx.multiplicativeExpression()[1].toLocation()), + fileType + ) + ) + } } } diff --git a/core/src/main/kotlin/Mutation.kt b/core/src/main/kotlin/Mutation.kt index 04f7b4b8..5c4e9fcf 100644 --- a/core/src/main/kotlin/Mutation.kt +++ b/core/src/main/kotlin/Mutation.kt @@ -63,7 +63,8 @@ sealed class Mutation( PRIMITIVE_RETURN, TRUE_RETURN, FALSE_RETURN, NULL_RETURN, PLUS_TO_MINUS, REMOVE_RUNTIME_CHECK, REMOVE_METHOD, NEGATE_IF, NEGATE_WHILE, REMOVE_IF, REMOVE_LOOP, REMOVE_AND_OR, REMOVE_TRY, REMOVE_STATEMENT, - REMOVE_PLUS, REMOVE_BINARY, CHANGE_EQUALS + REMOVE_PLUS, REMOVE_BINARY, CHANGE_EQUALS, + SWAP_BREAK_CONTINUE, PLUS_OR_MINUS_ONE_TO_ZERO } var modified: String? = null @@ -184,6 +185,8 @@ val OTHER = setOf( Mutation.Type.REMOVE_PLUS, Mutation.Type.REMOVE_BINARY, Mutation.Type.CHANGE_EQUALS, + Mutation.Type.SWAP_BREAK_CONTINUE, + Mutation.Type.PLUS_OR_MINUS_ONE_TO_ZERO ) val ALL = PITEST + OTHER @@ -515,6 +518,51 @@ class SwapAndOr( } } +class SwapBreakContinue( + location: Location, + original: String, + fileType: Source.FileType +) : Mutation(Type.SWAP_BREAK_CONTINUE, location, original, fileType) { + override val preservesLength = false + override val estimatedCount = 1 + override val mightNotCompile = false + override val fixedCount = true + + override fun applyMutation(random: Random): String { + return when (original) { + "break" -> "continue" + "continue" -> "break" + else -> error("${javaClass.name} didn't find the expected text") + } + } + + companion object { + fun matches(contents: String) = contents in setOf("break", "continue") + } +} + +class PlusOrMinusOneToZero( + location: Location, + original: String, + fileType: Source.FileType +) : Mutation(Type.PLUS_OR_MINUS_ONE_TO_ZERO, location, original, fileType) { + override val preservesLength = false + override val estimatedCount = 1 + override val mightNotCompile = false + override val fixedCount = true + + override fun applyMutation(random: Random): String { + return when (original) { + "1" -> "0" + else -> error("${javaClass.name} didn't find the expected text: $original") + } + } + + companion object { + fun matches(contents: String) = contents == "1" + } +} + private val javaPrimitiveTypes = setOf("byte", "short", "int", "long", "float", "double", "char", "boolean") private val kotlinPrimitiveTypes = setOf("Byte", "Short", "Int", "Long", "Float", "Double", "Char", "Boolean") diff --git a/core/src/main/resources/edu.illinois.cs.cs125.jeed.core.version b/core/src/main/resources/edu.illinois.cs.cs125.jeed.core.version index 95c1e3b8..25ee76e6 100644 --- a/core/src/main/resources/edu.illinois.cs.cs125.jeed.core.version +++ b/core/src/main/resources/edu.illinois.cs.cs125.jeed.core.version @@ -1 +1 @@ -version=2021.7.7 \ No newline at end of file +version=2021.7.8 \ No newline at end of file diff --git a/core/src/test/kotlin/TestJavaMutater.kt b/core/src/test/kotlin/TestJavaMutater.kt index 3833847c..faf7de42 100644 --- a/core/src/test/kotlin/TestJavaMutater.kt +++ b/core/src/test/kotlin/TestJavaMutater.kt @@ -437,6 +437,43 @@ public class Example { mutations[0].check(contents, "&&", "||") } } + "it should swap break and continue" { + Source.fromJava( + """ +public class Example { + public static int test(int first) { + for (int i = 0; i < 10; i++) { + if (i < 5) { + continue; + } + if (i > 7) { + break; + } + } + } +}""" + ).checkMutations { mutations, contents -> + mutations shouldHaveSize 2 + mutations[0].check(contents, "continue", "break") + } + } + "it should remove plus and minus 1" { + Source.fromJava( + """ +public class Example { + public static int test(int first) { + int i = 0; + int j = 0; + int i = i + 1; + int j = j - 1; + } +}""" + ).checkMutations { mutations, contents -> + mutations shouldHaveSize 2 + mutations[0].check(contents, "1", "0") + mutations[1].check(contents, "1", "0") + } + } "it should remove loops correctly" { Source.fromJava( """ diff --git a/core/src/test/kotlin/TestKotlinMutater.kt b/core/src/test/kotlin/TestKotlinMutater.kt index 83f37135..a6a9f0a5 100644 --- a/core/src/test/kotlin/TestKotlinMutater.kt +++ b/core/src/test/kotlin/TestKotlinMutater.kt @@ -438,7 +438,7 @@ fun test(first: Int) { } } - "it should flip and and or" { + "it should swap and and or" { Source.fromKotlin( """ fun test(first: Int) { @@ -453,6 +453,42 @@ fun test(first: Int) { } } + "it should swap break and continue" { + Source.fromKotlin( + """ +fun test(first: Int) { + for (i in 0..10) { + if (i < 5) { + continue + } + if (i > 7) { + break + } + } +} +""".trim() + ).checkMutations { mutations, contents -> + mutations shouldHaveSize 2 + mutations[0].check(contents, "continue", "break") + } + } + + "it should remove plus and minus 1" { + Source.fromKotlin( + """ +fun test() { + var i = 0 + var j = 0 + var i = i + 1 + var j = j - 1 +}""" + ).checkMutations { mutations, contents -> + mutations shouldHaveSize 2 + mutations[0].check(contents, "1", "0") + mutations[1].check(contents, "1", "0") + } + } + "it should remove loops correctly" { Source.fromKotlin( """ @@ -656,11 +692,12 @@ fun testing(): Int { } """.trim() ).also { source -> - source.mutater(types = ALL - setOf(Mutation.Type.REMOVE_METHOD, Mutation.Type.REMOVE_STATEMENT)).also { mutater -> - mutater.size shouldBe 2 - mutater.apply() - mutater.size shouldBe 0 - } + source.mutater(types = ALL - setOf(Mutation.Type.REMOVE_METHOD, Mutation.Type.REMOVE_STATEMENT)) + .also { mutater -> + mutater.size shouldBe 2 + mutater.apply() + mutater.size shouldBe 0 + } } } @@ -673,7 +710,10 @@ fun testing(): Int { } """.trim() ).also { source -> - source.mutater(shuffle = false, types = ALL - setOf(Mutation.Type.REMOVE_METHOD, Mutation.Type.REMOVE_STATEMENT)) + source.mutater( + shuffle = false, + types = ALL - setOf(Mutation.Type.REMOVE_METHOD, Mutation.Type.REMOVE_STATEMENT) + ) .also { mutater -> mutater.size shouldBe 3 mutater.apply() diff --git a/core/src/test/kotlin/TestSourceUtilities.kt b/core/src/test/kotlin/TestSourceUtilities.kt index 57ed3835..902684b8 100644 --- a/core/src/test/kotlin/TestSourceUtilities.kt +++ b/core/src/test/kotlin/TestSourceUtilities.kt @@ -36,6 +36,20 @@ class TestSourceUtilities : StringSpec({ Source.fromKotlin("""println("Hello, world!") // test me""").stripComments().contents shouldBe """println("Hello, world!") """ } + "should strip kotlin comments without breaking whitespace" { + Source.fromKotlin( + """ + |for (i in test) { + | println(i) // test me + |} + """.trimMargin() + ).stripComments().contents shouldBe + """ + |for (i in test) { + | println(i) + |} + """.trimMargin() + } "should diff text with same line count" { val first = """ |test me diff --git a/server/src/main/resources/edu.illinois.cs.cs125.jeed.server.version b/server/src/main/resources/edu.illinois.cs.cs125.jeed.server.version index 95c1e3b8..25ee76e6 100644 --- a/server/src/main/resources/edu.illinois.cs.cs125.jeed.server.version +++ b/server/src/main/resources/edu.illinois.cs.cs125.jeed.server.version @@ -1 +1 @@ -version=2021.7.7 \ No newline at end of file +version=2021.7.8 \ No newline at end of file