Skip to content

Commit 51c544d

Browse files
add markdown export to CompletenessTest
1 parent cff6ae1 commit 51c544d

File tree

2 files changed

+83
-19
lines changed

2 files changed

+83
-19
lines changed

language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ class RxScalaDemo extends JUnitSuite {
369369
println(s"Work took ${System.currentTimeMillis()-t0} ms")
370370
}
371371

372+
@Test def toSortedList() {
373+
assertEquals(Seq(7, 8, 9, 10), Observable(10, 7, 8, 9).toSeq.map(_.sorted).toBlockingObservable.single)
374+
val f = (a: Int, b: Int) => b < a
375+
assertEquals(Seq(10, 9, 8, 7), Observable(10, 7, 8, 9).toSeq.map(_.sortWith(f)).toBlockingObservable.single)
376+
}
377+
372378
def output(s: String): Unit = println(s)
373379

374380
// blocks until obs has completed

language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ import scala.collection.SortedSet
88
import scala.collection.SortedMap
99
import org.junit.Ignore
1010
import java.lang.reflect.Modifier
11+
import java.util.Date
12+
import java.util.Calendar
1113

1214
class CompletenessTest extends JUnitSuite {
1315

1416
val unnecessary = "[considered unnecessary in Scala land]"
1517

1618
val deprecated = "[deprecated in RxJava]"
1719

18-
val averageProblem = "[We can't have a general average method because Scala's Numeric does not have " +
19-
"scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum). " +
20-
"You can use fold instead to accumulate sum and numberOfElements and divide at the end.]"
20+
val averageProblem = "[We can't have a general average method because Scala's `Numeric` does not have " +
21+
"scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). " +
22+
"You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]"
2123

22-
val commentForFirstWithPredicate = "[use .filter(condition).first]"
24+
val commentForFirstWithPredicate = "[use `.filter(condition).first`]"
2325

2426
val correspondence = defaultMethodCorrespondence ++ Map(
2527
// manually added entries for Java instance methods
@@ -32,10 +34,10 @@ class CompletenessTest extends JUnitSuite {
3234
"dematerialize()" -> "dematerialize(<:<[T, Notification[U]])",
3335
"first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate,
3436
"firstOrDefault(T)" -> "firstOrElse(=> U)",
35-
"firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use .filter(condition).firstOrElse(default)]",
36-
"groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "groupBy(T => K)",
37+
"firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use `.filter(condition).firstOrElse(default)`]",
38+
"groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "[use `groupBy` and `map`]",
3739
"mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])",
38-
"mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine zipWithIndex with map or with a for comprehension]",
40+
"mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine `zipWithIndex` with `map` or with a for comprehension]",
3941
"onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])",
4042
"onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])",
4143
"onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)",
@@ -49,13 +51,13 @@ class CompletenessTest extends JUnitSuite {
4951
"skip(Int)" -> "drop(Int)",
5052
"skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)",
5153
"skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary,
52-
"startWith(Iterable[T])" -> "[unnecessary because we can just use ++ instead]",
54+
"startWith(Iterable[T])" -> "[unnecessary because we can just use `++` instead]",
5355
"takeFirst()" -> "first",
5456
"takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate,
5557
"takeLast(Int)" -> "takeRight(Int)",
5658
"toList()" -> "toSeq",
57-
"toSortedList()" -> unnecessary,
58-
"toSortedList(Func2[_ >: T, _ >: T, Integer])" -> unnecessary,
59+
"toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]",
60+
"toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]",
5961
"where(Func1[_ >: T, Boolean])" -> "filter(T => Boolean)",
6062
"window(Long, Long, TimeUnit)" -> "window(Duration, Duration)",
6163
"window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)",
@@ -81,24 +83,24 @@ class CompletenessTest extends JUnitSuite {
8183
"mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])",
8284
"mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> "flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])",
8385
"range(Int, Int)" -> "apply(Range)",
84-
"sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use (first zip second) map (p => p._1 == p._2)]",
85-
"sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use (first zip second) map (p => equality(p._1, p._2))]",
86+
"sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use `(first zip second) map (p => p._1 == p._2)`]",
87+
"sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use `(first zip second) map (p => equality(p._1, p._2))`]",
8688
"sum(Observable[Integer])" -> "sum(Numeric[U])",
8789
"sumDoubles(Observable[Double])" -> "sum(Numeric[U])",
8890
"sumFloats(Observable[Float])" -> "sum(Numeric[U])",
8991
"sumLongs(Observable[Long])" -> "sum(Numeric[U])",
9092
"synchronize(Observable[T])" -> "synchronize",
9193
"switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated,
9294
"switchOnNext(Observable[_ <: Observable[_ <: T]])" -> "switch(<:<[Observable[T], Observable[Observable[U]]])",
93-
"zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]",
94-
"zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]",
95-
"zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]"
95+
"zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method `zip` and `map`]",
96+
"zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]",
97+
"zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]"
9698
) ++ List.iterate("T", 9)(s => s + ", T").map(
9799
// all 9 overloads of startWith:
98-
"startWith(" + _ + ")" -> "[unnecessary because we can just use ++ instead]"
100+
"startWith(" + _ + ")" -> "[unnecessary because we can just use `++` instead]"
99101
).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map(
100102
// concat 2-9
101-
"concat(" + _ + ")" -> "[unnecessary because we can use ++ instead or Observable(o1, o2, ...).concat]"
103+
"concat(" + _ + ")" -> "[unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat`]"
102104
).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map(
103105
// all 10 overloads of from:
104106
"from(" + _ + ")" -> "apply(T*)"
@@ -109,10 +111,10 @@ class CompletenessTest extends JUnitSuite {
109111
("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary)
110112
}).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map(
111113
// merge 3-9:
112-
"merge(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flatten instead]"
114+
"merge(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flatten` instead]"
113115
).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map(
114116
// mergeDelayError 3-9:
115-
"mergeDelayError(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flattenDelayError instead]"
117+
"mergeDelayError(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead]"
116118
).drop(2).toMap ++ (3 to 9).map(i => {
117119
// combineLatest 3-9:
118120
val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("")
@@ -257,6 +259,12 @@ class CompletenessTest extends JUnitSuite {
257259
val status = if (bad == 0) "SUCCESS" else "BAD"
258260
println(s"\n$status: $bad out of ${bad+good} methods were not found in Scala Observable")
259261
}
262+
263+
def setTodoForMissingMethods(corresp: Map[String, String]): Map[String, String] = {
264+
val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet
265+
for ((javaM, scalaM) <- corresp) yield
266+
(javaM, if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') scalaM else "[**TODO: missing**]")
267+
}
260268

261269
@Test def checkJavaMethodPresence: Unit = {
262270
println("\nTesting that all mentioned Java methods exist")
@@ -269,4 +277,54 @@ class CompletenessTest extends JUnitSuite {
269277
checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]])
270278
}
271279

280+
def scalaToJavaSignature(s: String) =
281+
s.replaceAllLiterally("_ <:", "? extends")
282+
.replaceAllLiterally("_ >:", "? super")
283+
.replaceAllLiterally("[", "<")
284+
.replaceAllLiterally("]", ">")
285+
.replaceAllLiterally("Array<T>", "T[]")
286+
287+
def escapeJava(s: String) =
288+
s.replaceAllLiterally("<", "&lt;")
289+
.replaceAllLiterally(">", "&gt;")
290+
291+
@Test def printMarkdownCorrespondenceTable() {
292+
def isInteresting(p: (String, String)): Boolean =
293+
p._1.replaceAllLiterally("()", "") != p._2
294+
def groupingKey(p: (String, String)): (String, String) =
295+
(if (p._1.startsWith("average")) "average" else p._1.takeWhile(_ != '('), p._2)
296+
def formatJavaCol(name: String, alternatives: Iterable[String]): String = {
297+
alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => {
298+
if (s.length > 50) {
299+
val toolTip = escapeJava(s)
300+
"<span title=\"" + toolTip + "\"><code>" + name + "(...)</code></span>"
301+
} else {
302+
"`" + s + "`"
303+
}
304+
}).mkString("<br/>")
305+
}
306+
def formatScalaCol(s: String): String =
307+
if (s.startsWith("[") && s.endsWith("]")) s.drop(1).dropRight(1) else "`" + s + "`"
308+
def escape(s: String) = s.replaceAllLiterally("[", "&lt;").replaceAllLiterally("]", "&gt;")
309+
310+
println("""
311+
## Comparison of Scala Observable and Java Observable
312+
313+
Note:
314+
* This table contains both static methods and instance methods.
315+
* If a signature is too long, move your mouse over it to get the full signature.
316+
317+
318+
| Java Method | Scala Method |
319+
|-------------|--------------|""")
320+
321+
val ps = setTodoForMissingMethods(correspondence)
322+
323+
(for (((javaName, scalaCol), pairs) <- ps.groupBy(groupingKey(_)).toList.sortBy(_._1._1)) yield {
324+
"| " + formatJavaCol(javaName, pairs.map(_._1)) + " | " + formatScalaCol(scalaCol) + " |"
325+
}).foreach(println(_))
326+
println(s"\nThis table was generated on ${Calendar.getInstance().getTime()}.")
327+
println(s"**Do not edit**. Instead, edit `${getClass().getCanonicalName()}`.")
328+
}
329+
272330
}

0 commit comments

Comments
 (0)