Skip to content

Commit c0c5818

Browse files
authored
Merge pull request temyers#136 from jesperpi/feature/modulo-counter
Modulo Counter enhancement Fixes temyers#135
2 parents 9b3fabe + 4c9ece2 commit c0c5818

File tree

13 files changed

+355
-0
lines changed

13 files changed

+355
-0
lines changed

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ The naming scheme used for the generated files is controlled by the `namingSchem
161161
The following tokens can be used in the pattern:
162162
* `{f}` Converts the feature file name to a valid class name using the rules for feature-title, apart from the one up counter.
163163
* `{c}` Adds a one up counter. |
164+
* `{c:n}` Adds a one up counter modulo n (useful for selecting tests for parallelisation). |
164165

165166
By default, generated test files use the `simple` naming strategy.
166167

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.github.temyers.it</groupId>
8+
<artifactId>simple-it</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<description>A simple IT verifying the basic use case.</description>
12+
13+
<properties>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
<cucumber.version>1.2.2</cucumber.version>
16+
</properties>
17+
18+
<dependencies>
19+
<dependency>
20+
<groupId>info.cukes</groupId>
21+
<artifactId>cucumber-junit</artifactId>
22+
<version>${cucumber.version}</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>info.cukes</groupId>
26+
<artifactId>cucumber-java</artifactId>
27+
<version>${cucumber.version}</version>
28+
</dependency>
29+
</dependencies>
30+
31+
<build>
32+
<plugins>
33+
<plugin>
34+
<groupId>@project.groupId@</groupId>
35+
<artifactId>@project.artifactId@</artifactId>
36+
<version>@project.version@</version>
37+
<executions>
38+
<execution>
39+
<id>generateRunners</id>
40+
<phase>generate-test-sources</phase>
41+
<goals>
42+
<goal>generateRunners</goal>
43+
</goals>
44+
<configuration>
45+
<glue>
46+
<package>foo</package>
47+
<package>bar</package>
48+
</glue>
49+
<namingScheme>pattern</namingScheme>
50+
<namingPattern>Group{c:3}Parallel{c}IT</namingPattern>
51+
</configuration>
52+
</execution>
53+
</executions>
54+
</plugin>
55+
</plugins>
56+
</build>
57+
</project>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Feature: Feature1
2+
3+
Scenario: Generate Junit Runner for each feature file
4+
Given I have feature files
5+
When I generate Maven sources
6+
Then the file "target/generated-test-sources/1IT.java" should exist
7+
And it should contain:
8+
"""
9+
@RunWith(Cucumber.class)
10+
@CucumberOptions(
11+
strict = true,
12+
features = {"classpath:features/feature1.feature"},
13+
format = {"json:target/cucumber-parallel/1.json",
14+
"pretty"},
15+
monochrome = false,
16+
tags = {"@complete", "@accepted"},
17+
glue = {"foo", "bar"})
18+
public class Parallel01IT {
19+
}
20+
"""
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Feature: Feature2
2+
3+
Scenario: Generate Junit Runner for each feature file
4+
Given I have feature files
5+
When I generate Maven sources
6+
Then the file "target/generated-test-sources/1IT.java" should exist
7+
And it should contain:
8+
"""
9+
@RunWith(Cucumber.class)
10+
@CucumberOptions(
11+
strict = true,
12+
features = {"classpath:features/feature2.feature"},
13+
format = {"json:target/cucumber-parallel/2.json", "pretty"},
14+
monochrome = false,
15+
tags = {"@complete", "@accepted"},
16+
glue = {"foo", "bar"})
17+
public class Parallel02IT {
18+
}
19+
"""
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Feature: Feature3
2+
3+
Scenario: Generate Junit Runner for each feature file
4+
Given I have feature files
5+
When I generate Maven sources
6+
Then the file "target/generated-test-sources/1IT.java" should exist
7+
And it should contain:
8+
"""
9+
@RunWith(Cucumber.class)
10+
@CucumberOptions(
11+
strict = true,
12+
features = {"classpath:features/feature2.feature"},
13+
format = {"json:target/cucumber-parallel/2.json", "pretty"},
14+
monochrome = false,
15+
tags = {"@complete", "@accepted"},
16+
glue = {"foo", "bar"})
17+
public class Parallel02IT {
18+
}
19+
"""
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Feature: Feature4
2+
3+
Scenario: Generate Junit Runner for each feature file
4+
Given I have feature files
5+
When I generate Maven sources
6+
Then the file "target/generated-test-sources/1IT.java" should exist
7+
And it should contain:
8+
"""
9+
@RunWith(Cucumber.class)
10+
@CucumberOptions(
11+
strict = true,
12+
features = {"classpath:features/feature1.feature"},
13+
format = {"json:target/cucumber-parallel/1.json",
14+
"pretty"},
15+
monochrome = false,
16+
tags = {"@complete", "@accepted"},
17+
glue = {"foo", "bar"})
18+
public class Parallel01IT {
19+
}
20+
"""
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Feature: Feature5
2+
3+
Scenario: Generate Junit Runner for each feature file
4+
Given I have feature files
5+
When I generate Maven sources
6+
Then the file "target/generated-test-sources/1IT.java" should exist
7+
And it should contain:
8+
"""
9+
@RunWith(Cucumber.class)
10+
@CucumberOptions(
11+
strict = true,
12+
features = {"classpath:features/feature2.feature"},
13+
format = {"json:target/cucumber-parallel/2.json", "pretty"},
14+
monochrome = false,
15+
tags = {"@complete", "@accepted"},
16+
glue = {"foo", "bar"})
17+
public class Parallel02IT {
18+
}
19+
"""
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Feature: Feature6
2+
3+
Scenario: Generate Junit Runner for each feature file
4+
Given I have feature files
5+
When I generate Maven sources
6+
Then the file "target/generated-test-sources/1IT.java" should exist
7+
And it should contain:
8+
"""
9+
@RunWith(Cucumber.class)
10+
@CucumberOptions(
11+
strict = true,
12+
features = {"classpath:features/feature2.feature"},
13+
format = {"json:target/cucumber-parallel/2.json", "pretty"},
14+
monochrome = false,
15+
tags = {"@complete", "@accepted"},
16+
glue = {"foo", "bar"})
17+
public class Parallel02IT {
18+
}
19+
"""
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import org.junit.Assert
2+
3+
import static org.hamcrest.Matchers.equalToIgnoringWhiteSpace
4+
5+
File buildDirectory = new File(basedir, "target");
6+
7+
File suite01 = new File(basedir, "target/generated-test-sources/cucumber/Group0Parallel01IT.java")
8+
File suite02 = new File(basedir, "target/generated-test-sources/cucumber/Group1Parallel02IT.java")
9+
File suite03 = new File(basedir, "target/generated-test-sources/cucumber/Group2Parallel03IT.java")
10+
File suite04 = new File(basedir, "target/generated-test-sources/cucumber/Group0Parallel04IT.java")
11+
File suite05 = new File(basedir, "target/generated-test-sources/cucumber/Group1Parallel05IT.java")
12+
File suite06 = new File(basedir, "target/generated-test-sources/cucumber/Group2Parallel06IT.java")
13+
14+
15+
File feature1 = new File(basedir, "/src/test/resources/features/feature1.feature")
16+
File feature2 = new File(basedir, "/src/test/resources/features/feature2.feature")
17+
18+
assert suite01.isFile()
19+
assert suite02.isFile()
20+
assert suite03.isFile()
21+
assert suite04.isFile()
22+
assert suite05.isFile()
23+
assert suite06.isFile()
24+
25+
String expected01 =
26+
"""import org.junit.runner.RunWith;
27+
28+
import cucumber.api.CucumberOptions;
29+
import cucumber.api.junit.Cucumber;
30+
31+
@RunWith(Cucumber.class)
32+
@CucumberOptions(
33+
strict = true,
34+
features = {"${feature1.absolutePath}"},
35+
plugin = {"json:${buildDirectory.absolutePath}/cucumber-parallel/1.json"},
36+
monochrome = false,
37+
tags = {},
38+
glue = {"foo", "bar"})
39+
public class Group0Parallel01IT {
40+
}"""
41+
42+
String expected02 =
43+
"""import org.junit.runner.RunWith;
44+
45+
import cucumber.api.CucumberOptions;
46+
import cucumber.api.junit.Cucumber;
47+
48+
@RunWith(Cucumber.class)
49+
@CucumberOptions(
50+
strict = true,
51+
features = {"${feature2.absolutePath}"},
52+
plugin = {"json:${buildDirectory.absolutePath}/cucumber-parallel/2.json"},
53+
monochrome = false,
54+
tags = {},
55+
glue = {"foo", "bar"})
56+
public class Group1Parallel02IT {
57+
}"""
58+
59+
// Depending on the OS, listFiles can list files in different order. The actual order of files isn't necessary
60+
61+
Assert.assertThat(suite01.text, equalToIgnoringWhiteSpace(expected01))
62+
Assert.assertThat(suite02.text, equalToIgnoringWhiteSpace(expected02))
63+
64+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.github.timm.cucumber;
2+
3+
import com.github.timm.cucumber.generate.name.Counter;
4+
5+
import java.util.regex.Matcher;
6+
import java.util.regex.Pattern;
7+
8+
/**
9+
* This class counts modulo n, where n is an integer extracted from the configured naming pattern
10+
* using regex "{c:(\d*)}".
11+
*/
12+
public class ModuloCounter implements Counter {
13+
14+
private final int module;
15+
private int counter = 0;
16+
17+
/**
18+
* Generates a counter from the pattern string.
19+
* The pattern string is used to infer the module to count over.
20+
* @param patternString the configure naming pattern string
21+
*/
22+
public ModuloCounter(String patternString) {
23+
this.module = getModulo(patternString);
24+
}
25+
26+
public int next() {
27+
return this.counter++ % this.module;
28+
}
29+
30+
private int getModulo(String extractFrom) {
31+
Matcher matcher = Pattern.compile(".*\\{c:(\\d*)}.*").matcher(extractFrom);
32+
return matcher.matches()
33+
? Integer.decode(matcher.group(1))
34+
: 1;
35+
}
36+
}

src/main/java/com/github/timm/cucumber/generate/name/PatternNamingScheme.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.github.timm.cucumber.generate.name;
22

3+
import com.github.timm.cucumber.ModuloCounter;
4+
35
/**
46
* Generate a Class Name based on a pattern.
57
*
@@ -13,12 +15,14 @@
1315
* <ul>
1416
* <li>'{f}' - The feature file, converted using supplied naming scheem.</li>
1517
* <li>'{c}' - A one-up number, formatted to have minimum 2 character width.</li>
18+
* <li>'{c:n}' - A one-up number modulo n, with no minimum character width.</li>
1619
* </ul>
1720
*/
1821
public class PatternNamingScheme implements ClassNamingScheme {
1922

2023
private final String pattern;
2124
private final Counter counter;
25+
private final Counter moduloCounter;
2226
private final ClassNamingScheme featureFileNamingScheme;
2327

2428
/**
@@ -31,6 +35,7 @@ public PatternNamingScheme(final String pattern, final Counter counter,
3135

3236
this.pattern = pattern;
3337
this.counter = counter;
38+
this.moduloCounter = new ModuloCounter(pattern);
3439
this.featureFileNamingScheme = featureFileNamingScheme;
3540
}
3641

@@ -43,6 +48,7 @@ public String generate(final String featureFileName) {
4348
String className =
4449
pattern.replace("{f}", featureFileNamingScheme.generate(featureFileName));
4550
className = className.replace("{c}", String.format("%02d", counter.next()));
51+
className = className.replaceAll("\\{c:\\d*}", String.format("%d", moduloCounter.next()));
4652
return className;
4753
}
4854

0 commit comments

Comments
 (0)