From d324e0922fe798151b94f2f0efdf8c7dfa9c67ee Mon Sep 17 00:00:00 2001 From: Nhat Date: Tue, 12 Sep 2023 12:18:04 +0200 Subject: [PATCH] feat(parser-js): Initial support for the `v` flag for `unicodeSets` --- README.md | 129 +++++++++--------- .../scala/weaponregex/parser/ParserJS.scala | 4 +- .../weaponregex/parser/ParserJSTest.scala | 128 ++++++++++------- docs/README.md | 129 +++++++++--------- 4 files changed, 211 insertions(+), 179 deletions(-) diff --git a/README.md b/README.md index 75638ba..ea29821 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ to support [Stryker Mutator](https://github.com/stryker-mutator). Weapon regeX i JavaScript and Scala and is used in [Stryker4s](https://github.com/stryker-mutator/stryker4s) and [StrykerJS](https://github.com/stryker-mutator/stryker-js) flavors of Stryker. The JavaScript version of the library is generated from Scala using [Scala.js](https://www.scala-js.org/). -The generated mutant regular expressions cover human errors, such as edge cases and typos. A list of provided mutators is given below. +The generated mutant regular expressions cover human errors, such as edge cases and typos. A list of provided mutators +is given below. For an introduction to mutation testing, see [Stryker's website](https://stryker-mutator.io/). The current supported versions for Scala are: `2.12`, `2.13` and `3`. @@ -22,7 +23,8 @@ In case you want to incorporate Weapon-regeX into your project. ## Scala -Add Weapon regeX to your `build.sbt` [![Maven Central](https://img.shields.io/maven-central/v/io.stryker-mutator/weapon-regex_3.svg?label=Maven%20Central&colorB=brightgreen)](https://search.maven.org/artifact/io.stryker-mutator/weapon-regex_3): +Add Weapon regeX to +your `build.sbt` [![Maven Central](https://img.shields.io/maven-central/v/io.stryker-mutator/weapon-regex_3.svg?label=Maven%20Central&colorB=brightgreen)](https://search.maven.org/artifact/io.stryker-mutator/weapon-regex_3): ```scala libraryDependencies += "io.stryker-mutator" %% "weapon-regex" % weaponRegexVersion @@ -34,8 +36,8 @@ Mutate! import weaponregex.WeaponRegeX WeaponRegeX.mutate("^abc(d+|[xyz])$") match { - case Right(mutants) => mutants map (_.pattern) - case Left(e) => throw new RuntimeException(e) + case Right(mutants) => mutants map (_.pattern) + case Left(e) => throw new RuntimeException(e) } // res0: Seq[String] = List( // "abc(d+|[xyz])$", @@ -72,7 +74,7 @@ import wrx from 'weapon-regex'; let mutants = wrx.mutate('^abc(d+|[xyz])$'); mutants.forEach((mutant) => { - console.log(mutant.pattern); + console.log(mutant.pattern); }); ``` @@ -92,11 +94,11 @@ import weaponregex.mutator.BuiltinMutators import weaponregex.parser.{ParserFlavor, ParserFlavorJVM} def mutate( - pattern: String, - mutators: Seq[TokenMutator] = BuiltinMutators.all, - mutationLevels: Seq[Int] = null, - flavor: ParserFlavor = ParserFlavorJVM -): Either[String, Seq[Mutant]] = ??? + pattern : String, + mutators : Seq[TokenMutator] = BuiltinMutators.all, + mutationLevels: Seq[Int] = null, + flavor : ParserFlavor = ParserFlavorJVM + ): Either[String, Seq[Mutant]] = ??? ``` With the `mutators` argument you can give a select list of mutators that should be used in @@ -109,24 +111,27 @@ based on the levels in the list. If omitted, no filtering takes place. The `flavor` argument allows setting the parser flavor that will be used to parse the pattern. Currently, we support a `ParserFlavorJVM` and `ParserFlavorJS`. By default in Scala the JVM flavor is used. -This function will return a `Right` with `Seq[Mutant]` if it can be parsed, or a `Left` with the error message otherwise. +This function will return a `Right` with `Seq[Mutant]` if it can be parsed, or a `Left` with the error message +otherwise. ## JavaScript -The `mutate` function can be called with regular expression flags and an options object to control which mutators and which parser flavor should be used in the mutation process: +The `mutate` function can be called with regular expression flags and an options object to control which mutators and +which parser flavor should be used in the mutation process: ```js import wrx from 'weapon-regex'; let mutants = wrx.mutate('^abc(d+|[xyz])$', 'u', { - mutators: Array.from(wrx.mutators.values()), - mutationLevels: [1, 2, 3], - flavor: ParserFlavorJS, + mutators: Array.from(wrx.mutators.values()), + mutationLevels: [1, 2, 3], + flavor: ParserFlavorJS, }); ``` Both options can be omitted, and have the same functionality as the options described in the Scala -API section. By default in JS the JS parser flavor is used. You can get a map of mutators from the `mutators` attribute of the library. It is +API section. By default in JS the JS parser flavor is used. You can get a map of mutators from the `mutators` attribute +of the library. It is a `Map` from string (mutator name) to a mutator object. This function will return a JavaScript Array of `Mutant` if it can be parsed, or throw an exception otherwise. @@ -135,30 +140,30 @@ This function will return a JavaScript Array of `Mutant` if it can be parsed, or All the supported mutators and at which mutation level they appear are shown in the table below. -| Name | 1 | 2 | 3 | -| --------------------------------------------------------------- | --- | --- | --- | -| [BOLRemoval](#bolremoval) | ✅ | ✅ | ✅ | -| [EOLRemoval](#eolremoval) | ✅ | ✅ | ✅ | -| [BOL2BOI](#bol2boi) | | ✅ | ✅ | -| [EOL2EOI](#eol2eoi) | | ✅ | ✅ | -| [CharClassNegation](#charclassnegation) | ✅ | -| [CharClassChildRemoval](#charclasschildremoval) | | ✅ | ✅ | -| [CharClassAnyChar](#charclassanychar) | | ✅ | ✅ | -| [CharClassRangeModification](#charclassrangemodification) | | | ✅ | -| [PredefCharClassNegation](#predefcharclassnegation) | ✅ | -| [PredefCharClassNullification](#predefcharclassnullification) | | ✅ | ✅ | -| [PredefCharClassAnyChar](#predefcharclassanychar) | | ✅ | ✅ | -| [UnicodeCharClassNegation](#unicodecharclassnegation) | ✅ | -| [QuantifierRemoval](#quantifierremoval) | ✅ | -| [QuantifierNChange](#quantifiernchange) | | ✅ | ✅ | -| [QuantifierNOrMoreModification](#quantifiernormoremodification) | | ✅ | ✅ | -| [QuantifierNOrMoreChange](#quantifiernormorechange) | | ✅ | ✅ | -| [QuantifierNMModification](#quantifiernmmodification) | | ✅ | ✅ | -| [QuantifierShortModification](#quantifiershortmodification) | | ✅ | ✅ | -| [QuantifierShortChange](#quantifiershortchange) | | ✅ | ✅ | -| [QuantifierReluctantAddition](#quantifierreluctantaddition) | | | ✅ | -| [GroupToNCGroup](#grouptoncgroup) | | ✅ | ✅ | -| [LookaroundNegation](#lookaroundnegation) | ✅ | ✅ | ✅ | +| Name | 1 | 2 | 3 | +|-----------------------------------------------------------------|---|---|---| +| [BOLRemoval](#bolremoval) | ✅ | ✅ | ✅ | +| [EOLRemoval](#eolremoval) | ✅ | ✅ | ✅ | +| [BOL2BOI](#bol2boi) | | ✅ | ✅ | +| [EOL2EOI](#eol2eoi) | | ✅ | ✅ | +| [CharClassNegation](#charclassnegation) | ✅ | +| [CharClassChildRemoval](#charclasschildremoval) | | ✅ | ✅ | +| [CharClassAnyChar](#charclassanychar) | | ✅ | ✅ | +| [CharClassRangeModification](#charclassrangemodification) | | | ✅ | +| [PredefCharClassNegation](#predefcharclassnegation) | ✅ | +| [PredefCharClassNullification](#predefcharclassnullification) | | ✅ | ✅ | +| [PredefCharClassAnyChar](#predefcharclassanychar) | | ✅ | ✅ | +| [UnicodeCharClassNegation](#unicodecharclassnegation) | ✅ | +| [QuantifierRemoval](#quantifierremoval) | ✅ | +| [QuantifierNChange](#quantifiernchange) | | ✅ | ✅ | +| [QuantifierNOrMoreModification](#quantifiernormoremodification) | | ✅ | ✅ | +| [QuantifierNOrMoreChange](#quantifiernormorechange) | | ✅ | ✅ | +| [QuantifierNMModification](#quantifiernmmodification) | | ✅ | ✅ | +| [QuantifierShortModification](#quantifiershortmodification) | | ✅ | ✅ | +| [QuantifierShortChange](#quantifiershortchange) | | ✅ | ✅ | +| [QuantifierReluctantAddition](#quantifierreluctantaddition) | | | ✅ | +| [GroupToNCGroup](#grouptoncgroup) | | ✅ | ✅ | +| [LookaroundNegation](#lookaroundnegation) | ✅ | ✅ | ✅ | ## Boundary Mutators @@ -167,7 +172,7 @@ All the supported mutators and at which mutation level they appear are shown in Remove the beginning of line character `^`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `^abc` | `abc` | [Back to table 🔝](#supported-mutators) @@ -177,7 +182,7 @@ Remove the beginning of line character `^`. Remove the end of line character `$`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `abc$` | `abc` | [Back to table 🔝](#supported-mutators) @@ -187,7 +192,7 @@ Remove the end of line character `$`. Change the beginning of line character `^` to a beginning of input character `\A`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `^abc` | `\Aabc` | [Back to table 🔝](#supported-mutators) @@ -197,7 +202,7 @@ Change the beginning of line character `^` to a beginning of input character `\A Change the end of line character `^` to a end of input character `\z`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `abc$` | `abc\z` | [Back to table 🔝](#supported-mutators) @@ -209,7 +214,7 @@ Change the end of line character `^` to a end of input character `\z`. Flips the sign of a character class. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `[abc]` | `[^abc]` | | `[^abc]` | `[abc]` | @@ -220,7 +225,7 @@ Flips the sign of a character class. Remove a child of a character class. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `[abc]` | `[bc]` | | `[abc]` | `[ac]` | | `[abc]` | `[ab]` | @@ -232,7 +237,7 @@ Remove a child of a character class. Change a character class to a character class which matches any character. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `[abc]` | `[\w\W]` | [Back to table 🔝](#supported-mutators) @@ -242,7 +247,7 @@ Change a character class to a character class which matches any character. Change the high and low of a range by one in both directions if possible. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `[b-y]` | `[a-y]` | | `[b-y]` | `[c-y]` | | `[b-y]` | `[b-z]` | @@ -257,7 +262,7 @@ Change the high and low of a range by one in both directions if possible. Flips the sign of a predefined character class. All the predefined character classes are shown in the table below. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `\d` | `\D` | | `\D` | `\d` | | `\s` | `\S` | @@ -272,7 +277,7 @@ Flips the sign of a predefined character class. All the predefined character cla Remove the backslash from a predefined character class such as `\w`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `\d` | `d` | | `\D` | `D` | | `\s` | `s` | @@ -288,7 +293,7 @@ Change a predefined character class to a character class containing the predefin negation. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `\d` | `[\d\D]` | | `\D` | `[\D\d]` | | `\s` | `[\s\S]` | @@ -303,7 +308,7 @@ negation. Flips the sign of a Unicode character class. | Original | Mutated | -| ----------- | ----------- | +|-------------|-------------| | `\p{Alpha}` | `\P{Alpha}` | | `\P{Alpha}` | `\p{Alpha}` | @@ -317,7 +322,7 @@ Remove a quantifier. This is done for all possible quantifiers, even ranges, and and possessive variants. | Original | Mutated | -| ----------- | ------- | +|-------------|---------| | `abc?` | `abc` | | `abc*` | `abc` | | `abc+` | `abc` | @@ -338,7 +343,7 @@ and possessive variants. Change the fixed amount quantifier to a couple of range variants. | Original | Mutated | -| -------- | ---------- | +|----------|------------| | `abc{9}` | `abc{0,9}` | | `abc{9}` | `abc{9,}` | @@ -350,7 +355,7 @@ Change the `n` to infinity range quantifier to a couple of variants where the lo incremented and decremented by one. | Original | Mutated | -| --------- | ---------- | +|-----------|------------| | `abc{9,}` | `abc{8,}` | | `abc{9,}` | `abc{10,}` | @@ -361,7 +366,7 @@ incremented and decremented by one. Turn an `n` or more range quantifier into a fixed number quantifier. | Original | Mutated | -| --------- | -------- | +|-----------|----------| | `abc{9,}` | `abc{9}` | [Back to table 🔝](#supported-mutators) @@ -372,7 +377,7 @@ Alter the `n` to `m` range quantifier by decrementing or incrementing the high a range by one. | Original | Mutated | -| ---------- | ----------- | +|------------|-------------| | `abc{3,9}` | `abc{2,9}` | | `abc{3,9}` | `abc{4,9}` | | `abc{3,9}` | `abc{3,8}` | @@ -387,7 +392,7 @@ variant (`{0,1}`, `{0,}`, `{1,}`), and applies the same mutations as mentioned i above. | Original | Mutated | -| -------- | ---------- | +|----------|------------| | `abc?` | `abc{1,1}` | | `abc?` | `abc{0,0}` | | `abc?` | `abc{0,2}` | @@ -402,7 +407,7 @@ above. Change the shorthand quantifiers `*` and `+` to their fixed range quantifier variant. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `abc*` | `abc{0}` | | `abc+` | `abc{1}` | @@ -413,7 +418,7 @@ Change the shorthand quantifiers `*` and `+` to their fixed range quantifier var Change greedy quantifiers to reluctant quantifiers. | Original | Mutated | -| ----------- | ------------ | +|-------------|--------------| | `abc?` | `abc??` | | `abc*` | `abc*?` | | `abc+` | `abc+?` | @@ -430,7 +435,7 @@ Change greedy quantifiers to reluctant quantifiers. Change a normal group to a non-capturing group. | Original | Mutated | -| -------- | --------- | +|----------|-----------| | `(abc)` | `(?:abc)` | [Back to table 🔝](#supported-mutators) @@ -440,7 +445,7 @@ Change a normal group to a non-capturing group. Flips the sign of a lookaround (lookahead, lookbehind) construct. | Original | Mutated | -| ---------- | ---------- | +|------------|------------| | `(?=abc)` | `(?!abc)` | | `(?!abc)` | `(?=abc)` | | `(?<=abc)` | `(? f.contains("u") || f.contains("v")) /** Regex special characters */ diff --git a/core/src/test/scala/weaponregex/parser/ParserJSTest.scala b/core/src/test/scala/weaponregex/parser/ParserJSTest.scala index 7e213fb..830f41e 100644 --- a/core/src/test/scala/weaponregex/parser/ParserJSTest.scala +++ b/core/src/test/scala/weaponregex/parser/ParserJSTest.scala @@ -63,7 +63,7 @@ class ParserJSTest extends munit.FunSuite with ParserTest { treeBuildTest(parsedTree, pattern) } - test("Parse non-hexadecimal value `\\xGG` without the Unicode flag") { + test("Parse non-hexadecimal value `\\xGG` without the Unicode flag") { val pattern = "\\xGG" val parsedTree = Parser(pattern, parserFlavor).getOrFail.to[Concat] @@ -131,90 +131,108 @@ class ParserJSTest extends munit.FunSuite with ParserTest { test("Parse `\\u{20}` as Unicode character with the Unicode flag") { val pattern = "\\u{20}" - val parsedTree = Parser(pattern, Some("ug"), parserFlavor).getOrFail.to[MetaChar] - assertEquals(parsedTree.metaChar, "u{20}") + Seq("u", "v").foreach(flag => { + val parsedTree = Parser(pattern, Some(flag + "g"), parserFlavor).getOrFail.to[MetaChar] - treeBuildTest(parsedTree, pattern) + assertEquals(parsedTree.metaChar, "u{20}") + + treeBuildTest(parsedTree, pattern) + }) } test("Unparsable: `\\x{20}` with the Unicode flag") { val pattern = "\\x{20}" parseErrorTest(pattern, Some("u")) + parseErrorTest(pattern, Some("v")) } test("Unparsable: `\\u20` with the Unicode flag") { val pattern = "\\u20" parseErrorTest(pattern, Some("u")) + parseErrorTest(pattern, Some("v")) } test("Unparsable: out-of-range code point hexadecimal values with the Unicode flag") { val pattern = "\\u{110000}" // 10FFFF + 1 parseErrorTest(pattern, Some("u")) + parseErrorTest(pattern, Some("v")) } test("Parse character class with Unicode character classes with lone properties, with the Unicode flag") { val pattern = """[\p{Alpha}\P{hello_World_0123}]""" - val parsedTree = Parser(pattern, Some("u"), parserFlavor).getOrFail.to[CharacterClass] - assert(clue(parsedTree.children.head) match { - case UnicodeCharClass("Alpha", _, true, "") => true - case _ => false - }) - assert(clue(parsedTree.children.last) match { - case UnicodeCharClass("hello_World_0123", _, false, "") => true - case _ => false - }) + Seq("u", "v").foreach(flag => { + val parsedTree = Parser(pattern, Some(flag), parserFlavor).getOrFail.to[CharacterClass] - treeBuildTest(parsedTree, pattern) + assert(clue(parsedTree.children.head) match { + case UnicodeCharClass("Alpha", _, true, "") => true + case _ => false + }) + assert(clue(parsedTree.children.last) match { + case UnicodeCharClass("hello_World_0123", _, false, "") => true + case _ => false + }) + + treeBuildTest(parsedTree, pattern) + }) } test("Parse character class with Unicode character classes with properties and values, with the Unicode flag") { val pattern = """[\p{Script_Extensions=Latin}\P{hello_World_0123=Goodbye_world_321}]""" - val parsedTree = Parser(pattern, Some("u"), parserFlavor).getOrFail.to[CharacterClass] - assert(clue(parsedTree.children.head) match { - case UnicodeCharClass("Script_Extensions", _, true, "Latin") => true - case _ => false - }) - assert(clue(parsedTree.children.last) match { - case UnicodeCharClass("hello_World_0123", _, false, "Goodbye_world_321") => true - case _ => false - }) + Seq("u", "v").foreach(flag => { + val parsedTree = Parser(pattern, Some(flag), parserFlavor).getOrFail.to[CharacterClass] - treeBuildTest(parsedTree, pattern) + assert(clue(parsedTree.children.head) match { + case UnicodeCharClass("Script_Extensions", _, true, "Latin") => true + case _ => false + }) + assert(clue(parsedTree.children.last) match { + case UnicodeCharClass("hello_World_0123", _, false, "Goodbye_world_321") => true + case _ => false + }) + + treeBuildTest(parsedTree, pattern) + }) } test("Parse Unicode character classes with lone properties, with the Unicode flag") { val pattern = """\p{Alpha}\P{hello_World_0123}""" - val parsedTree = Parser(pattern, Some("u"), parserFlavor).getOrFail.to[Concat] - assert(clue(parsedTree.children.head) match { - case UnicodeCharClass("Alpha", _, true, "") => true - case _ => false - }) - assert(clue(parsedTree.children.last) match { - case UnicodeCharClass("hello_World_0123", _, false, "") => true - case _ => false - }) + Seq("u", "v").foreach(flag => { + val parsedTree = Parser(pattern, Some(flag), parserFlavor).getOrFail.to[Concat] - treeBuildTest(parsedTree, pattern) + assert(clue(parsedTree.children.head) match { + case UnicodeCharClass("Alpha", _, true, "") => true + case _ => false + }) + assert(clue(parsedTree.children.last) match { + case UnicodeCharClass("hello_World_0123", _, false, "") => true + case _ => false + }) + + treeBuildTest(parsedTree, pattern) + }) } test("Parse Unicode character classes with properties and values, with the Unicode flag") { val pattern = """\p{Script_Extensions=Latin}\P{hello_World_0123=Goodbye_world_321}""" - val parsedTree = Parser(pattern, Some("u"), parserFlavor).getOrFail.to[Concat] - assert(clue(parsedTree.children.head) match { - case UnicodeCharClass("Script_Extensions", _, true, "Latin") => true - case _ => false - }) - assert(clue(parsedTree.children.last) match { - case UnicodeCharClass("hello_World_0123", _, false, "Goodbye_world_321") => true - case _ => false - }) + Seq("u", "v").foreach(flag => { + val parsedTree = Parser(pattern, Some(flag), parserFlavor).getOrFail.to[Concat] - treeBuildTest(parsedTree, pattern) + assert(clue(parsedTree.children.head) match { + case UnicodeCharClass("Script_Extensions", _, true, "Latin") => true + case _ => false + }) + assert(clue(parsedTree.children.last) match { + case UnicodeCharClass("hello_World_0123", _, false, "Goodbye_world_321") => true + case _ => false + }) + + treeBuildTest(parsedTree, pattern) + }) } test("Parse `\\p{Alpha}` as a character quotation in a character class without the Unicode flag") { @@ -360,22 +378,26 @@ class ParserJSTest extends munit.FunSuite with ParserTest { test("Parse syntax characters escape with the Unicode flag") { val syntaxChars = """^$\.*+?()[]{}|/""" val pattern = "\\" + syntaxChars.mkString("\\") - val parsedTree = Parser(pattern, Some("u"), parserFlavor).getOrFail.to[Concat] - syntaxChars zip parsedTree.children foreach { case (char, child) => - assert(clue(child) match { - case MetaChar(c, _) => c.head == char - case QuoteChar(c, _) => c == char - case _ => false - }) - } + Seq("u", "v").foreach(flag => { + val parsedTree = Parser(pattern, Some(flag), parserFlavor).getOrFail.to[Concat] - treeBuildTest(parsedTree, pattern) + syntaxChars zip parsedTree.children foreach { case (char, child) => + assert(clue(child) match { + case MetaChar(c, _) => c.head == char + case QuoteChar(c, _) => c == char + case _ => false + }) + } + + treeBuildTest(parsedTree, pattern) + }) } test("Unparsable: non-syntax character escape with the Unicode flag") { val pattern = "\\a" parseErrorTest(pattern, Some("u")) + parseErrorTest(pattern, Some("v")) } test("Unparsable: long-quantifier-like with nothing preceding") { diff --git a/docs/README.md b/docs/README.md index 0641364..238524b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,7 +11,8 @@ to support [Stryker Mutator](https://github.com/stryker-mutator). Weapon regeX i JavaScript and Scala and is used in [Stryker4s](https://github.com/stryker-mutator/stryker4s) and [StrykerJS](https://github.com/stryker-mutator/stryker-js) flavors of Stryker. The JavaScript version of the library is generated from Scala using [Scala.js](https://www.scala-js.org/). -The generated mutant regular expressions cover human errors, such as edge cases and typos. A list of provided mutators is given below. +The generated mutant regular expressions cover human errors, such as edge cases and typos. A list of provided mutators +is given below. For an introduction to mutation testing, see [Stryker's website](https://stryker-mutator.io/). The current supported versions for Scala are: `2.12`, `2.13` and `3`. @@ -22,7 +23,8 @@ In case you want to incorporate Weapon-regeX into your project. ## Scala -Add Weapon regeX to your `build.sbt` [![Maven Central](https://img.shields.io/maven-central/v/io.stryker-mutator/weapon-regex_3.svg?label=Maven%20Central&colorB=brightgreen)](https://search.maven.org/artifact/io.stryker-mutator/weapon-regex_3): +Add Weapon regeX to +your `build.sbt` [![Maven Central](https://img.shields.io/maven-central/v/io.stryker-mutator/weapon-regex_3.svg?label=Maven%20Central&colorB=brightgreen)](https://search.maven.org/artifact/io.stryker-mutator/weapon-regex_3): ```scala libraryDependencies += "io.stryker-mutator" %% "weapon-regex" % weaponRegexVersion @@ -34,8 +36,8 @@ Mutate! import weaponregex.WeaponRegeX WeaponRegeX.mutate("^abc(d+|[xyz])$") match { - case Right(mutants) => mutants map (_.pattern) - case Left(e) => throw new RuntimeException(e) + case Right(mutants) => mutants map (_.pattern) + case Left(e) => throw new RuntimeException(e) } ``` @@ -55,7 +57,7 @@ import wrx from 'weapon-regex'; let mutants = wrx.mutate('^abc(d+|[xyz])$'); mutants.forEach((mutant) => { - console.log(mutant.pattern); + console.log(mutant.pattern); }); ``` @@ -75,11 +77,11 @@ import weaponregex.mutator.BuiltinMutators import weaponregex.parser.{ParserFlavor, ParserFlavorJVM} def mutate( - pattern: String, - mutators: Seq[TokenMutator] = BuiltinMutators.all, - mutationLevels: Seq[Int] = null, - flavor: ParserFlavor = ParserFlavorJVM -): Either[String, Seq[Mutant]] = ??? + pattern : String, + mutators : Seq[TokenMutator] = BuiltinMutators.all, + mutationLevels: Seq[Int] = null, + flavor : ParserFlavor = ParserFlavorJVM + ): Either[String, Seq[Mutant]] = ??? ``` With the `mutators` argument you can give a select list of mutators that should be used in @@ -92,24 +94,27 @@ based on the levels in the list. If omitted, no filtering takes place. The `flavor` argument allows setting the parser flavor that will be used to parse the pattern. Currently, we support a `ParserFlavorJVM` and `ParserFlavorJS`. By default in Scala the JVM flavor is used. -This function will return a `Right` with `Seq[Mutant]` if it can be parsed, or a `Left` with the error message otherwise. +This function will return a `Right` with `Seq[Mutant]` if it can be parsed, or a `Left` with the error message +otherwise. ## JavaScript -The `mutate` function can be called with regular expression flags and an options object to control which mutators and which parser flavor should be used in the mutation process: +The `mutate` function can be called with regular expression flags and an options object to control which mutators and +which parser flavor should be used in the mutation process: ```js import wrx from 'weapon-regex'; let mutants = wrx.mutate('^abc(d+|[xyz])$', 'u', { - mutators: Array.from(wrx.mutators.values()), - mutationLevels: [1, 2, 3], - flavor: ParserFlavorJS, + mutators: Array.from(wrx.mutators.values()), + mutationLevels: [1, 2, 3], + flavor: ParserFlavorJS, }); ``` Both options can be omitted, and have the same functionality as the options described in the Scala -API section. By default in JS the JS parser flavor is used. You can get a map of mutators from the `mutators` attribute of the library. It is +API section. By default in JS the JS parser flavor is used. You can get a map of mutators from the `mutators` attribute +of the library. It is a `Map` from string (mutator name) to a mutator object. This function will return a JavaScript Array of `Mutant` if it can be parsed, or throw an exception otherwise. @@ -118,30 +123,30 @@ This function will return a JavaScript Array of `Mutant` if it can be parsed, or All the supported mutators and at which mutation level they appear are shown in the table below. -| Name | 1 | 2 | 3 | -| --------------------------------------------------------------- | --- | --- | --- | -| [BOLRemoval](#bolremoval) | ✅ | ✅ | ✅ | -| [EOLRemoval](#eolremoval) | ✅ | ✅ | ✅ | -| [BOL2BOI](#bol2boi) | | ✅ | ✅ | -| [EOL2EOI](#eol2eoi) | | ✅ | ✅ | -| [CharClassNegation](#charclassnegation) | ✅ | -| [CharClassChildRemoval](#charclasschildremoval) | | ✅ | ✅ | -| [CharClassAnyChar](#charclassanychar) | | ✅ | ✅ | -| [CharClassRangeModification](#charclassrangemodification) | | | ✅ | -| [PredefCharClassNegation](#predefcharclassnegation) | ✅ | -| [PredefCharClassNullification](#predefcharclassnullification) | | ✅ | ✅ | -| [PredefCharClassAnyChar](#predefcharclassanychar) | | ✅ | ✅ | -| [UnicodeCharClassNegation](#unicodecharclassnegation) | ✅ | -| [QuantifierRemoval](#quantifierremoval) | ✅ | -| [QuantifierNChange](#quantifiernchange) | | ✅ | ✅ | -| [QuantifierNOrMoreModification](#quantifiernormoremodification) | | ✅ | ✅ | -| [QuantifierNOrMoreChange](#quantifiernormorechange) | | ✅ | ✅ | -| [QuantifierNMModification](#quantifiernmmodification) | | ✅ | ✅ | -| [QuantifierShortModification](#quantifiershortmodification) | | ✅ | ✅ | -| [QuantifierShortChange](#quantifiershortchange) | | ✅ | ✅ | -| [QuantifierReluctantAddition](#quantifierreluctantaddition) | | | ✅ | -| [GroupToNCGroup](#grouptoncgroup) | | ✅ | ✅ | -| [LookaroundNegation](#lookaroundnegation) | ✅ | ✅ | ✅ | +| Name | 1 | 2 | 3 | +|-----------------------------------------------------------------|---|---|---| +| [BOLRemoval](#bolremoval) | ✅ | ✅ | ✅ | +| [EOLRemoval](#eolremoval) | ✅ | ✅ | ✅ | +| [BOL2BOI](#bol2boi) | | ✅ | ✅ | +| [EOL2EOI](#eol2eoi) | | ✅ | ✅ | +| [CharClassNegation](#charclassnegation) | ✅ | +| [CharClassChildRemoval](#charclasschildremoval) | | ✅ | ✅ | +| [CharClassAnyChar](#charclassanychar) | | ✅ | ✅ | +| [CharClassRangeModification](#charclassrangemodification) | | | ✅ | +| [PredefCharClassNegation](#predefcharclassnegation) | ✅ | +| [PredefCharClassNullification](#predefcharclassnullification) | | ✅ | ✅ | +| [PredefCharClassAnyChar](#predefcharclassanychar) | | ✅ | ✅ | +| [UnicodeCharClassNegation](#unicodecharclassnegation) | ✅ | +| [QuantifierRemoval](#quantifierremoval) | ✅ | +| [QuantifierNChange](#quantifiernchange) | | ✅ | ✅ | +| [QuantifierNOrMoreModification](#quantifiernormoremodification) | | ✅ | ✅ | +| [QuantifierNOrMoreChange](#quantifiernormorechange) | | ✅ | ✅ | +| [QuantifierNMModification](#quantifiernmmodification) | | ✅ | ✅ | +| [QuantifierShortModification](#quantifiershortmodification) | | ✅ | ✅ | +| [QuantifierShortChange](#quantifiershortchange) | | ✅ | ✅ | +| [QuantifierReluctantAddition](#quantifierreluctantaddition) | | | ✅ | +| [GroupToNCGroup](#grouptoncgroup) | | ✅ | ✅ | +| [LookaroundNegation](#lookaroundnegation) | ✅ | ✅ | ✅ | ## Boundary Mutators @@ -150,7 +155,7 @@ All the supported mutators and at which mutation level they appear are shown in Remove the beginning of line character `^`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `^abc` | `abc` | [Back to table 🔝](#supported-mutators) @@ -160,7 +165,7 @@ Remove the beginning of line character `^`. Remove the end of line character `$`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `abc$` | `abc` | [Back to table 🔝](#supported-mutators) @@ -170,7 +175,7 @@ Remove the end of line character `$`. Change the beginning of line character `^` to a beginning of input character `\A`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `^abc` | `\Aabc` | [Back to table 🔝](#supported-mutators) @@ -180,7 +185,7 @@ Change the beginning of line character `^` to a beginning of input character `\A Change the end of line character `^` to a end of input character `\z`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `abc$` | `abc\z` | [Back to table 🔝](#supported-mutators) @@ -192,7 +197,7 @@ Change the end of line character `^` to a end of input character `\z`. Flips the sign of a character class. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `[abc]` | `[^abc]` | | `[^abc]` | `[abc]` | @@ -203,7 +208,7 @@ Flips the sign of a character class. Remove a child of a character class. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `[abc]` | `[bc]` | | `[abc]` | `[ac]` | | `[abc]` | `[ab]` | @@ -215,7 +220,7 @@ Remove a child of a character class. Change a character class to a character class which matches any character. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `[abc]` | `[\w\W]` | [Back to table 🔝](#supported-mutators) @@ -225,7 +230,7 @@ Change a character class to a character class which matches any character. Change the high and low of a range by one in both directions if possible. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `[b-y]` | `[a-y]` | | `[b-y]` | `[c-y]` | | `[b-y]` | `[b-z]` | @@ -240,7 +245,7 @@ Change the high and low of a range by one in both directions if possible. Flips the sign of a predefined character class. All the predefined character classes are shown in the table below. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `\d` | `\D` | | `\D` | `\d` | | `\s` | `\S` | @@ -255,7 +260,7 @@ Flips the sign of a predefined character class. All the predefined character cla Remove the backslash from a predefined character class such as `\w`. | Original | Mutated | -| -------- | ------- | +|----------|---------| | `\d` | `d` | | `\D` | `D` | | `\s` | `s` | @@ -271,7 +276,7 @@ Change a predefined character class to a character class containing the predefin negation. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `\d` | `[\d\D]` | | `\D` | `[\D\d]` | | `\s` | `[\s\S]` | @@ -286,7 +291,7 @@ negation. Flips the sign of a Unicode character class. | Original | Mutated | -| ----------- | ----------- | +|-------------|-------------| | `\p{Alpha}` | `\P{Alpha}` | | `\P{Alpha}` | `\p{Alpha}` | @@ -300,7 +305,7 @@ Remove a quantifier. This is done for all possible quantifiers, even ranges, and and possessive variants. | Original | Mutated | -| ----------- | ------- | +|-------------|---------| | `abc?` | `abc` | | `abc*` | `abc` | | `abc+` | `abc` | @@ -321,7 +326,7 @@ and possessive variants. Change the fixed amount quantifier to a couple of range variants. | Original | Mutated | -| -------- | ---------- | +|----------|------------| | `abc{9}` | `abc{0,9}` | | `abc{9}` | `abc{9,}` | @@ -333,7 +338,7 @@ Change the `n` to infinity range quantifier to a couple of variants where the lo incremented and decremented by one. | Original | Mutated | -| --------- | ---------- | +|-----------|------------| | `abc{9,}` | `abc{8,}` | | `abc{9,}` | `abc{10,}` | @@ -344,7 +349,7 @@ incremented and decremented by one. Turn an `n` or more range quantifier into a fixed number quantifier. | Original | Mutated | -| --------- | -------- | +|-----------|----------| | `abc{9,}` | `abc{9}` | [Back to table 🔝](#supported-mutators) @@ -355,7 +360,7 @@ Alter the `n` to `m` range quantifier by decrementing or incrementing the high a range by one. | Original | Mutated | -| ---------- | ----------- | +|------------|-------------| | `abc{3,9}` | `abc{2,9}` | | `abc{3,9}` | `abc{4,9}` | | `abc{3,9}` | `abc{3,8}` | @@ -370,7 +375,7 @@ variant (`{0,1}`, `{0,}`, `{1,}`), and applies the same mutations as mentioned i above. | Original | Mutated | -| -------- | ---------- | +|----------|------------| | `abc?` | `abc{1,1}` | | `abc?` | `abc{0,0}` | | `abc?` | `abc{0,2}` | @@ -385,7 +390,7 @@ above. Change the shorthand quantifiers `*` and `+` to their fixed range quantifier variant. | Original | Mutated | -| -------- | -------- | +|----------|----------| | `abc*` | `abc{0}` | | `abc+` | `abc{1}` | @@ -396,7 +401,7 @@ Change the shorthand quantifiers `*` and `+` to their fixed range quantifier var Change greedy quantifiers to reluctant quantifiers. | Original | Mutated | -| ----------- | ------------ | +|-------------|--------------| | `abc?` | `abc??` | | `abc*` | `abc*?` | | `abc+` | `abc+?` | @@ -413,7 +418,7 @@ Change greedy quantifiers to reluctant quantifiers. Change a normal group to a non-capturing group. | Original | Mutated | -| -------- | --------- | +|----------|-----------| | `(abc)` | `(?:abc)` | [Back to table 🔝](#supported-mutators) @@ -423,7 +428,7 @@ Change a normal group to a non-capturing group. Flips the sign of a lookaround (lookahead, lookbehind) construct. | Original | Mutated | -| ---------- | ---------- | +|------------|------------| | `(?=abc)` | `(?!abc)` | | `(?!abc)` | `(?=abc)` | | `(?<=abc)` | `(?