Skip to content

Commit aa8e03f

Browse files
authored
3.x: Code verification fixes for javac's generated switchmap classes (#6570)
* 3.x: Code verification workarounds for Java 13+ compilation artifacts * Gradle/IntelliJ output dir corrections
1 parent 58b0014 commit aa8e03f

14 files changed

+134
-57
lines changed

src/test/java/io/reactivex/testsupport/TestHelper.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import static org.mockito.ArgumentMatchers.any;
1818
import static org.mockito.Mockito.mock;
1919

20+
import java.io.File;
2021
import java.lang.reflect.*;
22+
import java.net.URL;
2123
import java.util.*;
2224
import java.util.concurrent.*;
2325
import java.util.concurrent.atomic.AtomicInteger;
@@ -3218,4 +3220,39 @@ public static <T> TestObserverEx<T> assertValueSet(TestObserverEx<T> to, T... va
32183220
}
32193221
return to;
32203222
}
3223+
3224+
/**
3225+
* Given a base reactive type name, try to find its source in the current runtime
3226+
* path and return a file to it or null if not found.
3227+
* @param baseClassName the class name such as {@code Maybe}
3228+
* @return the File pointing to the source
3229+
* @throws Exception on error
3230+
*/
3231+
public static File findSource(String baseClassName) throws Exception {
3232+
URL u = TestHelper.class.getResource(TestHelper.class.getSimpleName() + ".class");
3233+
3234+
String path = new File(u.toURI()).toString().replace('\\', '/');
3235+
3236+
// System.out.println(path);
3237+
3238+
int i = path.toLowerCase().indexOf("/rxjava");
3239+
if (i < 0) {
3240+
System.out.println("Can't find the base RxJava directory");
3241+
return null;
3242+
}
3243+
3244+
// find end of any potential postfix to /RxJava
3245+
int j = path.indexOf("/", i + 6);
3246+
3247+
String p = path.substring(0, j + 1) + "src/main/java/io/reactivex/" + baseClassName + ".java";
3248+
3249+
File f = new File(p);
3250+
3251+
if (!f.canRead()) {
3252+
System.out.println("Can't read " + p);
3253+
return null;
3254+
}
3255+
3256+
return f;
3257+
}
32213258
}

src/test/java/io/reactivex/validators/CheckLocalVariablesInTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
import org.junit.Test;
2121

22+
import io.reactivex.testsupport.TestHelper;
23+
2224
/**
2325
* Checks for commonly copy-pasted but not-renamed local variables in unit tests.
2426
* <ul>
@@ -41,7 +43,7 @@ static void findPattern(String pattern) throws Exception {
4143
}
4244

4345
static void findPattern(String pattern, boolean checkMain) throws Exception {
44-
File f = MaybeNo2Dot0Since.findSource("Flowable");
46+
File f = TestHelper.findSource("Flowable");
4547
if (f == null) {
4648
System.out.println("Unable to find sources of RxJava");
4749
return;

src/test/java/io/reactivex/validators/FixLicenseHeaders.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import org.junit.Test;
2020

21+
import io.reactivex.testsupport.TestHelper;
22+
2123
/**
2224
* Adds license header to java files.
2325
*/
@@ -45,7 +47,7 @@ public void checkAndUpdateLicenses() throws Exception {
4547
// no point in changing the files in CI
4648
return;
4749
}
48-
File f = MaybeNo2Dot0Since.findSource("Flowable");
50+
File f = TestHelper.findSource("Flowable");
4951
if (f == null) {
5052
return;
5153
}

src/test/java/io/reactivex/validators/InternalWrongNaming.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818

1919
import org.junit.Test;
2020

21+
import io.reactivex.testsupport.TestHelper;
22+
2123
/**
2224
* Adds license header to java files.
2325
*/
2426
public class InternalWrongNaming {
2527

2628
static void checkInternalOperatorNaming(String baseClassName, String consumerClassName, String... ignore) throws Exception {
27-
File f = MaybeNo2Dot0Since.findSource(baseClassName);
29+
File f = TestHelper.findSource(baseClassName);
2830
if (f == null) {
2931
return;
3032
}

src/test/java/io/reactivex/validators/JavadocFindUnescapedAngleBrackets.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
import org.junit.Test;
1919

20+
import io.reactivex.testsupport.TestHelper;
21+
2022
public class JavadocFindUnescapedAngleBrackets {
2123

2224
@Test
2325
public void find() throws Exception {
24-
File base = MaybeNo2Dot0Since.findSource("Flowable");
26+
File base = TestHelper.findSource("Flowable");
2527

2628
if (base == null) {
2729
return;

src/test/java/io/reactivex/validators/JavadocForAnnotations.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.junit.*;
2121

2222
import io.reactivex.*;
23+
import io.reactivex.testsupport.TestHelper;
2324

2425
/**
2526
* Checks the source code of the base reactive types and locates missing
@@ -28,7 +29,7 @@
2829
public class JavadocForAnnotations {
2930

3031
static void checkSource(String baseClassName, boolean scheduler) throws Exception {
31-
File f = MaybeNo2Dot0Since.findSource(baseClassName);
32+
File f = TestHelper.findSource(baseClassName);
3233
if (f == null) {
3334
return;
3435
}
@@ -173,7 +174,7 @@ static final void scanForBadMethod(StringBuilder sourceCode, String annotation,
173174
}
174175

175176
static void checkSchedulerBadMethod(String baseClassName) throws Exception {
176-
File f = MaybeNo2Dot0Since.findSource(baseClassName);
177+
File f = TestHelper.findSource(baseClassName);
177178
if (f == null) {
178179
return;
179180
}

src/test/java/io/reactivex/validators/JavadocWording.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.junit.Assert.*;
2020
import org.junit.Test;
2121

22+
import io.reactivex.testsupport.TestHelper;
2223
import io.reactivex.validators.BaseTypeParser.RxMethod;
2324

2425
/**
@@ -38,7 +39,7 @@ public static int lineNumber(CharSequence s, int index) {
3839

3940
@Test
4041
public void maybeDocRefersToMaybeTypes() throws Exception {
41-
List<RxMethod> list = BaseTypeParser.parse(MaybeNo2Dot0Since.findSource("Maybe"), "Maybe");
42+
List<RxMethod> list = BaseTypeParser.parse(TestHelper.findSource("Maybe"), "Maybe");
4243

4344
assertFalse(list.isEmpty());
4445

@@ -214,7 +215,7 @@ public void maybeDocRefersToMaybeTypes() throws Exception {
214215

215216
@Test
216217
public void flowableDocRefersToFlowableTypes() throws Exception {
217-
List<RxMethod> list = BaseTypeParser.parse(MaybeNo2Dot0Since.findSource("Flowable"), "Flowable");
218+
List<RxMethod> list = BaseTypeParser.parse(TestHelper.findSource("Flowable"), "Flowable");
218219

219220
assertFalse(list.isEmpty());
220221

@@ -323,7 +324,7 @@ public void flowableDocRefersToFlowableTypes() throws Exception {
323324

324325
@Test
325326
public void observableDocRefersToObservableTypes() throws Exception {
326-
List<RxMethod> list = BaseTypeParser.parse(MaybeNo2Dot0Since.findSource("Observable"), "Observable");
327+
List<RxMethod> list = BaseTypeParser.parse(TestHelper.findSource("Observable"), "Observable");
327328

328329
assertFalse(list.isEmpty());
329330

@@ -424,7 +425,7 @@ public void observableDocRefersToObservableTypes() throws Exception {
424425

425426
@Test
426427
public void singleDocRefersToSingleTypes() throws Exception {
427-
List<RxMethod> list = BaseTypeParser.parse(MaybeNo2Dot0Since.findSource("Single"), "Single");
428+
List<RxMethod> list = BaseTypeParser.parse(TestHelper.findSource("Single"), "Single");
428429

429430
assertFalse(list.isEmpty());
430431

@@ -598,7 +599,7 @@ public void singleDocRefersToSingleTypes() throws Exception {
598599

599600
@Test
600601
public void completableDocRefersToCompletableTypes() throws Exception {
601-
List<RxMethod> list = BaseTypeParser.parse(MaybeNo2Dot0Since.findSource("Completable"), "Completable");
602+
List<RxMethod> list = BaseTypeParser.parse(TestHelper.findSource("Completable"), "Completable");
602603

603604
assertFalse(list.isEmpty());
604605

src/test/java/io/reactivex/validators/MaybeNo2Dot0Since.java

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,57 +16,22 @@
1616
import static org.junit.Assert.fail;
1717

1818
import java.io.*;
19-
import java.net.URL;
2019

2120
import org.junit.Test;
2221

2322
import io.reactivex.Maybe;
23+
import io.reactivex.testsupport.TestHelper;
2424

2525
/**
2626
* Checks the source code of Maybe and finds unnecessary since 2.0 annotations in the
2727
* method's javadocs.
2828
*/
2929
public class MaybeNo2Dot0Since {
3030

31-
/**
32-
* Given a base reactive type name, try to find its source in the current runtime
33-
* path and return a file to it or null if not found.
34-
* @param baseClassName the class name such as {@code Maybe}
35-
* @return the File pointing to the source
36-
* @throws Exception on error
37-
*/
38-
public static File findSource(String baseClassName) throws Exception {
39-
URL u = MaybeNo2Dot0Since.class.getResource(MaybeNo2Dot0Since.class.getSimpleName() + ".class");
40-
41-
String path = new File(u.toURI()).toString().replace('\\', '/');
42-
43-
// System.out.println(path);
44-
45-
int i = path.indexOf("/RxJava");
46-
if (i < 0) {
47-
System.out.println("Can't find the base RxJava directory");
48-
return null;
49-
}
50-
51-
// find end of any potential postfix to /RxJava
52-
int j = path.indexOf("/", i + 6);
53-
54-
String p = path.substring(0, j + 1) + "src/main/java/io/reactivex/" + baseClassName + ".java";
55-
56-
File f = new File(p);
57-
58-
if (!f.canRead()) {
59-
System.out.println("Can't read " + p);
60-
return null;
61-
}
62-
63-
return f;
64-
}
65-
6631
@Test
6732
public void noSince20InMaybe() throws Exception {
6833

69-
File f = findSource(Maybe.class.getSimpleName());
34+
File f = TestHelper.findSource(Maybe.class.getSimpleName());
7035

7136
String line;
7237

src/test/java/io/reactivex/validators/NewLinesBeforeAnnotation.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import org.junit.Test;
2020

21+
import io.reactivex.testsupport.TestHelper;
22+
2123
/**
2224
* These tests verify the code style that a typical closing curly brace
2325
* and the next annotation &#64; indicator
@@ -64,7 +66,7 @@ public void tooManyEmptyNewLines5() throws Exception {
6466
}
6567

6668
static void findPattern(int newLines) throws Exception {
67-
File f = MaybeNo2Dot0Since.findSource("Flowable");
69+
File f = TestHelper.findSource("Flowable");
6870
if (f == null) {
6971
System.out.println("Unable to find sources of RxJava");
7072
return;

src/test/java/io/reactivex/validators/NoAnonymousInnerClassesTest.java

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
package io.reactivex.validators;
1515

16-
import java.io.File;
16+
import java.io.*;
1717
import java.net.URL;
1818
import java.util.*;
1919

@@ -26,6 +26,17 @@ public void verify() throws Exception {
2626
URL u = NoAnonymousInnerClassesTest.class.getResource("/");
2727
File f = new File(u.toURI());
2828

29+
String fs = f.toString().toLowerCase().replace("\\", "/");
30+
31+
System.out.println("Found " + fs);
32+
33+
// running this particular test from IntelliJ will have the wrong class directory
34+
// gradle will generate test classes into a separate directory too
35+
int idx = fs.indexOf("/test");
36+
if (idx >= 0) {
37+
f = new File(fs.substring(0, idx));
38+
}
39+
2940
StringBuilder b = new StringBuilder("Anonymous inner classes found:");
3041

3142
Queue<File> queue = new ArrayDeque<File>();
@@ -50,16 +61,60 @@ public void verify() throws Exception {
5061
if (name.endsWith(".class") && name.contains("$")
5162
&& !name.contains("Perf") && !name.contains("Test")
5263
&& !name.startsWith("Test")) {
64+
String baseName = name.substring(0, name.length() - 6);
5365
String[] parts = name.split("\\$");
5466
for (String s : parts) {
5567
if (Character.isDigit(s.charAt(0))) {
5668
String n = f.getAbsolutePath().substring(prefix.length()).replace('\\', '.').replace('/', '.');
5769
if (n.startsWith(".")) {
5870
n = n.substring(1);
5971
}
60-
b.append("\r\n").append(n);
61-
count++;
62-
break;
72+
73+
// javac generates switch-map anonymous classes with the same $x.class pattern
74+
// we have to look into the file and search for $SwitchMap$
75+
76+
boolean found = false;
77+
78+
FileInputStream fin = new FileInputStream(f);
79+
try {
80+
byte[] data = new byte[fin.available()];
81+
fin.read(data);
82+
83+
String content = new String(data, "ISO-8859-1");
84+
85+
if (content.contains("$SwitchMap$")) {
86+
// the parent class can reference these synthetic inner classes
87+
// and thus they also have $SwitchMap$
88+
// but the synthetic inner classes should not have further inner classes
89+
90+
File[] filesInTheSameDir = f.getParentFile().listFiles();
91+
92+
for (File fsame : filesInTheSameDir) {
93+
String fsameName = fsame.getName();
94+
if (fsameName.endsWith(".class")) {
95+
fsameName = fsameName.substring(0, fsameName.length() - 6);
96+
97+
if (fsameName.startsWith(baseName)
98+
&& fsameName.length() > baseName.length() + 1
99+
&& fsameName.charAt(baseName.length()) == '$'
100+
&& Character.isDigit(fsameName.charAt(baseName.length() + 1))) {
101+
found = true;
102+
break;
103+
}
104+
}
105+
}
106+
} else {
107+
found = true;
108+
}
109+
} finally {
110+
fin.close();
111+
}
112+
113+
if (found) {
114+
b.append("\r\n").append(n);
115+
count++;
116+
break;
117+
}
63118
}
64119
}
65120
}

0 commit comments

Comments
 (0)