Skip to content

Commit d52bec7

Browse files
authored
Fix error when running JUnit 4 tests using @RunWith(Enclosed.class) (#74)
* Exclude all nested classes Add an exclude filter as suggested in junit-team/junit-framework#3537 * Add tests for JUnit 4 nested classes
1 parent ea0b1bc commit d52bec7

File tree

7 files changed

+316
-0
lines changed

7 files changed

+316
-0
lines changed

lib/src/main/java/com/exercism/junit/JUnitTestRunner.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.exercism.*;
44
import org.junit.platform.engine.TestExecutionResult;
55
import org.junit.platform.engine.TestTag;
6+
import org.junit.platform.engine.discovery.ClassNameFilter;
67
import org.junit.platform.engine.discovery.DiscoverySelectors;
78
import org.junit.platform.engine.reporting.ReportEntry;
89
import org.junit.platform.engine.support.descriptor.MethodSource;
@@ -30,6 +31,7 @@ public void test(ClassLoader classLoader, Set<Path> classpathRoots) {
3031
Thread.currentThread().setContextClassLoader(classLoader);
3132
var request = LauncherDiscoveryRequestBuilder.request()
3233
.selectors(DiscoverySelectors.selectClasspathRoots(classpathRoots))
34+
.filters(ClassNameFilter.excludeClassNamePatterns(".+\\$.+"))
3335
.configurationParameter("junit.platform.output.capture.stdout", "true")
3436
.build();
3537
var launcher = LauncherFactory.create();

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ include 'tests:example-success-java21'
1010
include 'tests:example-syntax-error'
1111
include 'tests:example-junit5'
1212
include 'tests:example-v3'
13+
include 'tests:junit4-enclosed'
1314
include 'tests:junit5-nested'
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"authors": [],
3+
"contributors": [
4+
"FridaTveit",
5+
"jmrunkle",
6+
"jtigger",
7+
"JTMaher2",
8+
"kytrinyx",
9+
"lemoncurry",
10+
"matthewmorgan",
11+
"mirkoperillo",
12+
"morrme",
13+
"msomji",
14+
"muzimuzhi",
15+
"RonakLakhotia",
16+
"SleeplessByte",
17+
"Smarticles101",
18+
"sshine",
19+
"stkent",
20+
"vdemeester",
21+
"Zaldrick"
22+
],
23+
"files": {
24+
"solution": [
25+
"src/main/java/Cipher.java"
26+
],
27+
"test": [
28+
"src/test/java/SimpleCipherTest.java"
29+
],
30+
"example": [
31+
".meta/src/reference/java/Cipher.java"
32+
],
33+
"invalidator": [
34+
"build.gradle"
35+
]
36+
},
37+
"blurb": "Implement a simple shift cipher like Caesar and a more secure substitution cipher.",
38+
"source": "Substitution Cipher at Wikipedia",
39+
"source_url": "https://en.wikipedia.org/wiki/Substitution_cipher"
40+
}

tests/junit4-enclosed/build.gradle

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apply plugin: "java"
2+
apply plugin: "eclipse"
3+
apply plugin: "idea"
4+
5+
// set default encoding to UTF-8
6+
compileJava.options.encoding = "UTF-8"
7+
compileTestJava.options.encoding = "UTF-8"
8+
9+
repositories {
10+
mavenCentral()
11+
}
12+
13+
dependencies {
14+
testImplementation "junit:junit:4.13"
15+
testImplementation "org.assertj:assertj-core:3.15.0"
16+
}
17+
18+
test {
19+
testLogging {
20+
exceptionFormat = 'full'
21+
showStandardStreams = true
22+
events = ["passed", "failed", "skipped"]
23+
}
24+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"status" : "pass",
3+
"tests" : [ {
4+
"name" : "cipherCanDoubleShiftEncode",
5+
"test_code" : "@Test\npublic void cipherCanDoubleShiftEncode() {\n String plainText = \"iamapandabear\";\n String cipherText = \"qayaeaagaciai\";\n assertThat(new Cipher(plainText).encode(plainText)).isEqualTo(cipherText);\n}",
6+
"status" : "pass"
7+
}, {
8+
"name" : "cipherCanDecode",
9+
"test_code" : "@Test\npublic void cipherCanDecode() {\n String plainText = \"abcdefghij\";\n String cipherText = \"aaaaaaaaaa\";\n assertThat(cipherWithDefaultKey.decode(plainText)).isEqualTo(cipherText);\n}",
10+
"status" : "pass"
11+
}, {
12+
"name" : "cipherCanEncode",
13+
"test_code" : "@Test\npublic void cipherCanEncode() {\n String plainText = \"aaaaaaaaaa\";\n String cipherText = \"abcdefghij\";\n assertThat(cipherWithDefaultKey.encode(plainText)).isEqualTo(cipherText);\n}",
14+
"status" : "pass"
15+
}, {
16+
"name" : "cipherMessageLongerThanKey",
17+
"test_code" : "@Test\npublic void cipherMessageLongerThanKey() {\n String plainText = \"iamapandabear\";\n String key = \"abc\";\n String cipherText = \"iboaqcnecbfcr\";\n assertThat(new Cipher(key).encode(plainText)).isEqualTo(cipherText);\n}",
18+
"status" : "pass"
19+
}, {
20+
"name" : "cipherIsReversibleGivenKey",
21+
"test_code" : "@Test\npublic void cipherIsReversibleGivenKey() {\n String plainText = \"abcdefghij\";\n assertThat(cipherWithDefaultKey.decode(cipherWithDefaultKey.encode(plainText))).isEqualTo(plainText);\n}",
22+
"status" : "pass"
23+
}, {
24+
"name" : "cipherCanWrapDecode",
25+
"test_code" : "@Test\npublic void cipherCanWrapDecode() {\n String plainText = \"zabcdefghi\";\n String cipherText = \"zzzzzzzzzz\";\n assertThat(cipherWithDefaultKey.decode(plainText)).isEqualTo(cipherText);\n}",
26+
"status" : "pass"
27+
}, {
28+
"name" : "cipherCanWrapEncode",
29+
"test_code" : "@Test\npublic void cipherCanWrapEncode() {\n String plainText = \"zzzzzzzzzz\";\n String cipherText = \"zabcdefghi\";\n assertThat(cipherWithDefaultKey.encode(plainText)).isEqualTo(cipherText);\n}",
30+
"status" : "pass"
31+
}, {
32+
"name" : "keyIsLowercaseLetters",
33+
"test_code" : "@Test\npublic void keyIsLowercaseLetters() {\n assertThat(cipherWithDefaultKey.getKey()).matches(\"^[a-z]+$\");\n}",
34+
"status" : "pass"
35+
}, {
36+
"name" : "cipherCanDecode",
37+
"test_code" : "@Test\npublic void cipherCanDecode() {\n String cipherText = \"aaaaaaaaaa\";\n assertThat(cipherWithDefaultKey.decode(cipherWithDefaultKey.getKey().substring(0, 10))).isEqualTo(cipherText);\n}",
38+
"status" : "pass"
39+
}, {
40+
"name" : "cipherCanEncode",
41+
"test_code" : "/**\n * Here we take advantage of the fact that plaintext of \"aaa...\" doesn't output the key. This is a critical\n * problem with shift ciphers, some characters will always output the key verbatim.\n */\n@Test\npublic void cipherCanEncode() {\n String plainText = \"aaaaaaaaaa\";\n String cipherText = cipherWithDefaultKey.getKey().substring(0, 10);\n assertThat(cipherWithDefaultKey.encode(plainText)).isEqualTo(cipherText);\n}",
42+
"status" : "pass"
43+
}, {
44+
"name" : "cipherIsReversible",
45+
"test_code" : "@Test\npublic void cipherIsReversible() {\n String plainText = \"abcdefghij\";\n assertThat(cipherWithDefaultKey.decode(cipherWithDefaultKey.encode(plainText))).isEqualTo(plainText);\n}",
46+
"status" : "pass"
47+
} ],
48+
"version" : 3
49+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import java.util.Random;
2+
import java.util.stream.IntStream;
3+
4+
public class Cipher {
5+
6+
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz";
7+
private static final Random random = new Random();
8+
9+
public String key;
10+
11+
public Cipher() {
12+
this.key = IntStream.range(0, 100)
13+
.map(x -> ALPHABET.toCharArray()[random.nextInt(ALPHABET.length())])
14+
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
15+
.toString();
16+
}
17+
18+
public Cipher(String key) {
19+
if (!isValidKey(key)) {
20+
throw new IllegalArgumentException("Invalid key");
21+
}
22+
23+
this.key = key;
24+
}
25+
26+
private static boolean isValidKey(String key) {
27+
return key.matches("^[a-z]+$");
28+
}
29+
30+
public String getKey() {
31+
return key;
32+
}
33+
34+
public String encode(String plainText) {
35+
StringBuilder ciphertext = new StringBuilder(plainText.length());
36+
String sameLengthKey = getSufficientlyLongKeyForPlaintext(plainText, key);
37+
38+
for (int index = 0; index < plainText.length(); index++) {
39+
ciphertext.append(encodeCharacter(plainText, index, sameLengthKey));
40+
}
41+
42+
return ciphertext.toString();
43+
}
44+
45+
private String getSufficientlyLongKeyForPlaintext(String plainText, String key) {
46+
StringBuilder longKeyBuilder = new StringBuilder(key.length());
47+
longKeyBuilder.append(key);
48+
if (plainText.length() > key.length()) {
49+
int difference = plainText.length() - key.length();
50+
for (int i = 0; i < difference; i = i + key.length()) {
51+
longKeyBuilder.append(key);
52+
}
53+
}
54+
return longKeyBuilder.toString();
55+
}
56+
57+
private char encodeCharacter(String plainText, int index, String key) {
58+
int alphabetIdx = ALPHABET.indexOf(plainText.toCharArray()[index]) + ALPHABET.indexOf(key.toCharArray()[index]);
59+
60+
if (alphabetIdx >= ALPHABET.length()) {
61+
alphabetIdx -= ALPHABET.length();
62+
}
63+
64+
return ALPHABET.toCharArray()[alphabetIdx];
65+
}
66+
67+
public String decode(String cipherText) {
68+
StringBuilder plainText = new StringBuilder(cipherText.length());
69+
70+
for (int i = 0; i < cipherText.length(); i++) {
71+
plainText.append(decodeCharacter(cipherText, i));
72+
}
73+
74+
return plainText.toString();
75+
}
76+
77+
private char decodeCharacter(String cipherText, int index) {
78+
int alphabetIdx = ALPHABET
79+
.indexOf(cipherText.toCharArray()[index]) - ALPHABET.indexOf(key.toCharArray()[index]);
80+
81+
if (alphabetIdx < 0) {
82+
alphabetIdx += ALPHABET.length();
83+
}
84+
85+
return ALPHABET.toCharArray()[alphabetIdx];
86+
}
87+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import org.junit.Before;
2+
import org.junit.Ignore;
3+
import org.junit.Test;
4+
import org.junit.experimental.runners.Enclosed;
5+
import org.junit.runner.RunWith;
6+
7+
import static org.assertj.core.api.Assertions.assertThat;
8+
9+
10+
@RunWith(Enclosed.class)
11+
public class SimpleCipherTest {
12+
public static class RandomKeyCipher {
13+
private Cipher cipherWithDefaultKey;
14+
15+
@Before
16+
public void setup() {
17+
cipherWithDefaultKey = new Cipher();
18+
}
19+
20+
/**
21+
* Here we take advantage of the fact that plaintext of "aaa..." doesn't output the key. This is a critical
22+
* problem with shift ciphers, some characters will always output the key verbatim.
23+
*/
24+
@Test
25+
public void cipherCanEncode() {
26+
String plainText = "aaaaaaaaaa";
27+
String cipherText = cipherWithDefaultKey.getKey().substring(0, 10);
28+
assertThat(cipherWithDefaultKey.encode(plainText)).isEqualTo(cipherText);
29+
}
30+
31+
@Ignore("Remove to run test")
32+
@Test
33+
public void cipherCanDecode() {
34+
String cipherText = "aaaaaaaaaa";
35+
assertThat(cipherWithDefaultKey.decode(cipherWithDefaultKey.getKey().substring(0, 10)))
36+
.isEqualTo(cipherText);
37+
}
38+
39+
@Ignore("Remove to run test")
40+
@Test
41+
public void cipherIsReversible() {
42+
String plainText = "abcdefghij";
43+
assertThat(cipherWithDefaultKey.decode(cipherWithDefaultKey.encode(plainText))).isEqualTo(plainText);
44+
}
45+
46+
@Ignore("Remove to run test")
47+
@Test
48+
public void keyIsLowercaseLetters() {
49+
assertThat(cipherWithDefaultKey.getKey()).matches("^[a-z]+$");
50+
}
51+
}
52+
53+
public static class SubstitutionCipher {
54+
private Cipher cipherWithDefaultKey = new Cipher("abcdefghij");
55+
56+
@Ignore("Remove to run test")
57+
@Test
58+
public void cipherCanEncode() {
59+
String plainText = "aaaaaaaaaa";
60+
String cipherText = "abcdefghij";
61+
assertThat(cipherWithDefaultKey.encode(plainText)).isEqualTo(cipherText);
62+
}
63+
64+
@Ignore("Remove to run test")
65+
@Test
66+
public void cipherCanDecode() {
67+
String plainText = "abcdefghij";
68+
String cipherText = "aaaaaaaaaa";
69+
assertThat(cipherWithDefaultKey.decode(plainText)).isEqualTo(cipherText);
70+
}
71+
72+
@Ignore("Remove to run test")
73+
@Test
74+
public void cipherIsReversibleGivenKey() {
75+
String plainText = "abcdefghij";
76+
assertThat(cipherWithDefaultKey.decode(cipherWithDefaultKey.encode(plainText))).isEqualTo(plainText);
77+
}
78+
79+
@Ignore("Remove to run test")
80+
@Test
81+
public void cipherCanDoubleShiftEncode() {
82+
String plainText = "iamapandabear";
83+
String cipherText = "qayaeaagaciai";
84+
assertThat(new Cipher(plainText).encode(plainText)).isEqualTo(cipherText);
85+
}
86+
87+
@Ignore("Remove to run test")
88+
@Test
89+
public void cipherCanWrapEncode() {
90+
String plainText = "zzzzzzzzzz";
91+
String cipherText = "zabcdefghi";
92+
assertThat(cipherWithDefaultKey.encode(plainText)).isEqualTo(cipherText);
93+
}
94+
95+
@Ignore("Remove to run test")
96+
@Test
97+
public void cipherCanWrapDecode() {
98+
String plainText = "zabcdefghi";
99+
String cipherText = "zzzzzzzzzz";
100+
assertThat(cipherWithDefaultKey.decode(plainText)).isEqualTo(cipherText);
101+
}
102+
103+
@Ignore("Remove to run test")
104+
@Test
105+
public void cipherMessageLongerThanKey() {
106+
String plainText = "iamapandabear";
107+
String key = "abc";
108+
String cipherText = "iboaqcnecbfcr";
109+
assertThat(new Cipher(key).encode(plainText)).isEqualTo(cipherText);
110+
}
111+
}
112+
}
113+

0 commit comments

Comments
 (0)