@@ -4,65 +4,167 @@ import scala.reflect.runtime.universe._
4
4
import org .scalatest .junit .JUnitSuite
5
5
import org .junit .Test
6
6
import rx .util .functions ._
7
+ import scala .collection .SortedSet
8
+ import scala .collection .SortedMap
9
+ import org .junit .Ignore
7
10
8
11
class CompletenessTest extends JUnitSuite {
9
12
10
- case class Op (name : String , argTypes : Type * ) {
11
- override def toString = name + argTypes.mkString(" (" , " , " , " )" )
13
+ val unnecessary = " [considered unnecessary in Scala land]"
14
+
15
+ val correspondence = defaultInstanceMethodCorrespondence ++ Map (
16
+ // manually added entries
17
+ " aggregate(Func2[T, T, T])" -> " reduce((U, U) => U)" ,
18
+ " aggregate(R, Func2[R, _ >: T, R])" -> " fold(R)((R, T) => R)" ,
19
+ " all(Func1[_ >: T, Boolean])" -> " forall(T => Boolean)" ,
20
+ " buffer(Long, Long, TimeUnit)" -> " buffer(Duration, Duration)" ,
21
+ " buffer(Long, Long, TimeUnit, Scheduler)" -> " buffer(Duration, Duration, Scheduler)" ,
22
+ " dematerialize()" -> " dematerialize(<:<[T, Notification[U]])" ,
23
+ " groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> " groupBy(T => K)" ,
24
+ " mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> " flatMap(T => Observable[R])" ,
25
+ " onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> " onErrorResumeNext(Throwable => Observable[U])" ,
26
+ " onErrorResumeNext(Observable[_ <: T])" -> " onErrorResumeNext(Observable[U])" ,
27
+ " onErrorReturn(Func1[Throwable, _ <: T])" -> " onErrorReturn(Throwable => U)" ,
28
+ " onExceptionResumeNext(Observable[_ <: T])" -> " onExceptionResumeNext(Observable[U])" ,
29
+ " reduce(Func2[T, T, T])" -> " reduce((U, U) => U)" ,
30
+ " reduce(R, Func2[R, _ >: T, R])" -> " fold(R)((R, T) => R)" ,
31
+ " scan(Func2[T, T, T])" -> unnecessary,
32
+ " scan(R, Func2[R, _ >: T, R])" -> " scan(R)((R, T) => R)" ,
33
+ " skip(Int)" -> " drop(Int)" ,
34
+ " skipWhile(Func1[_ >: T, Boolean])" -> " dropWhile(T => Boolean)" ,
35
+ " skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary,
36
+ " startWith(Iterable[T])" -> " [unnecessary because we can just use ++ instead]" ,
37
+ " toList()" -> " toSeq" ,
38
+ " toSortedList()" -> unnecessary,
39
+ " toSortedList(Func2[_ >: T, _ >: T, Integer])" -> unnecessary,
40
+ " where(Func1[_ >: T, Boolean])" -> " filter(T => Boolean)" ,
41
+ " window(Long, Long, TimeUnit)" -> " window(Duration, Duration)" ,
42
+ " window(Long, Long, TimeUnit, Scheduler)" -> " window(Duration, Duration, Scheduler)"
43
+ ) ++ List .iterate(" T" , 9 )(s => s + " , T" ).map(
44
+ // all 9 overloads of startWith:
45
+ " startWith(" + _ + " )" -> " [unnecessary because we can just use ++ instead]"
46
+ ).toMap
47
+
48
+ def removePackage (s : String ) = s.replaceAll(" (\\ w+\\ .)+(\\ w+)" , " $2" )
49
+
50
+ def methodMembersToMethodStrings (members : Iterable [Symbol ]): Iterable [String ] = {
51
+ for (member <- members; alt <- member.asTerm.alternatives) yield {
52
+ val m = alt.asMethod
53
+ // multiple parameter lists in case of curried functions
54
+ val paramListStrs = for (paramList <- m.paramss) yield {
55
+ paramList.map(
56
+ symb => removePackage(symb.typeSignature.toString.replaceAll(" ,(\\ S)" , " , $1" ))
57
+ ).mkString(" (" , " , " , " )" )
58
+ }
59
+ val name = alt.asMethod.name.decoded
60
+ name + paramListStrs.mkString(" " )
61
+ }
62
+ }
63
+
64
+ def getPublicInstanceMethods (tp : Type ): Iterable [String ] = {
65
+ // declarations: => only those declared in Observable
66
+ // members => also those of superclasses
67
+ methodMembersToMethodStrings(tp.declarations.filter(m => m.isMethod && m.isPublic))
12
68
}
13
69
14
- val correspondence = Map (
15
- Op (" toList" ) -> Op (" toSeq" ),
16
- Op (" window" , typeOf[Int ]) -> Op (" window" , typeOf[Int ])
17
- )
70
+ def getStaticMethods (tp : Type ): Iterable [String ] = {
71
+ ???
72
+ }
18
73
19
- def check (cond : Boolean , ifGood : String , ifBad : String ): Unit = {
20
- if (cond) println(ifGood) else println(ifBad)
74
+ @ Test def printJavaInstanceMethods : Unit = {
75
+ println(" \n Instance methods of rx.Observable" )
76
+ println( " ---------------------------------\n " )
77
+ getPublicInstanceMethods(typeOf[rx.Observable [_]]).toList.sorted.foreach(println(_))
21
78
}
22
79
23
- def checkOperatorPresence (op : Op , tp : Type ): Unit = {
24
- val paramTypeLists = for (alt <- tp.member(newTermName(op.name)).asTerm.alternatives)
25
- yield alt.asMethod.paramss.headOption match {
26
- case Some (paramList) => paramList.map(symb => symb.typeSignature)
27
- case None => List ()
28
- }
29
-
30
- println(paramTypeLists)
31
-
32
- check(paramTypeLists.contains(op.argTypes.toList),
33
- s " $op is present in $tp" , s " $op is NOT present in $tp" )
80
+ @ Test def printScalaInstanceMethods : Unit = {
81
+ println(" \n Instance methods of rx.lang.scala.Observable" )
82
+ println( " --------------------------------------------\n " )
83
+ getPublicInstanceMethods(typeOf[rx.lang.scala.Observable [_]]).toList.sorted.foreach(s => println(s))
34
84
}
35
85
36
- @ Test def test3 () {
37
- val javaObs = typeOf[rx.Observable [_]]
38
- val scalaObs = typeOf[rx.lang.scala.Observable [_]]
39
-
40
- for ((javaOp, scalaOp) <- correspondence) {
41
- checkOperatorPresence(javaOp, javaObs)
42
- checkOperatorPresence(scalaOp, scalaObs)
86
+ def javaMethodSignatureToScala (s : String ): String = {
87
+ s.replaceAllLiterally(" Long, TimeUnit" , " Duration" )
88
+ .replaceAll(" Action0" , " () => Unit" )
89
+ // nested [] can't be parsed with regex, but we still do it, please forgive us ;-)
90
+ .replaceAll(" Action1\\ [([^]]*)\\ ]" , " $1 => Unit" )
91
+ .replaceAll(" Action2\\ [([^]]*), ([^]]*)\\ ]" , " ($1, $2) => Unit" )
92
+ .replaceAll(" Func0\\ [([^]]*)\\ ]" , " () => $1" )
93
+ .replaceAll(" Func1\\ [([^]]*), ([^]]*)\\ ]" , " $1 => $2" )
94
+ .replaceAll(" Func2\\ [([^]]*), ([^]]*), ([^]]*)\\ ]" , " ($1, $2) => $3" )
95
+ .replaceAllLiterally(" _ <: " , " " )
96
+ .replaceAllLiterally(" _ >: " , " " )
97
+ .replaceAll(" (\\ w+)\\ (\\ )" , " $1" )
98
+ }
99
+
100
+ def defaultInstanceMethodCorrespondence : Map [String , String ] = {
101
+ val instanceMethods = getPublicInstanceMethods(typeOf[rx.Observable [_]]).toList.sorted
102
+ val tuples = for (javaM <- instanceMethods) yield (javaM, javaMethodSignatureToScala(javaM))
103
+ tuples.toMap
104
+ }
105
+
106
+ @ Test def printDefaultInstanceMethodCorrespondence : Unit = {
107
+ println(" \n Default Instance Method Correspondence" )
108
+ println( " --------------------------------------\n " )
109
+ val c = SortedMap (defaultInstanceMethodCorrespondence.toSeq : _* )
110
+ val len = c.keys.map(_.length).max + 2
111
+ for ((javaM, scalaM) <- c) {
112
+ println(s """ %- ${len}s -> %s, """ .format(" \" " + javaM + " \" " , " \" " + scalaM + " \" " ))
43
113
}
44
114
}
45
115
46
-
47
- @ Test def test1 () {
48
- val c = Class .forName(" rx.Observable" )
49
- for (method <- c.getMethods()) {
50
- println(method.getName())
116
+ @ Ignore // Does not yet work
117
+ @ Test def printJavaStaticMethods : Unit = {
118
+ println(" \n Static methods of rx.Observable" )
119
+ println( " -------------------------------\n " )
120
+ getStaticMethods(typeOf[rx.Observable [_]]).toList.sorted.foreach(println(_))
121
+ }
122
+
123
+ def checkMethodPresence (expectedMethods : Iterable [String ], tp : Type ): Unit = {
124
+ val actualMethods = getPublicInstanceMethods(tp).toSet
125
+ val expMethodsSorted = expectedMethods.toList.sorted
126
+ var good = 0
127
+ var bad = 0
128
+ for (m <- expMethodsSorted) if (actualMethods.contains(m) || m.charAt(0 ) == '[' ) {
129
+ good += 1
130
+ } else {
131
+ bad += 1
132
+ println(s " Warning: $m is NOT present in $tp" )
51
133
}
52
-
134
+ val status = if (bad == 0 ) " SUCCESS" else " BAD"
135
+ println(s " $status: $bad out of ${bad+ good} methods were not found in $tp" )
53
136
}
54
137
55
- @ Test def test2 () {
56
- val tp = typeOf[rx.Observable [_]]
57
- for (member <- tp.members) println(member)
58
- println(" 00000" )
59
- // println(tp.member(stringToTermName("all")))
60
- // println(tp.member("all"))
61
-
62
- val methodAll = tp.member(newTermName(" all" )).asMethod
63
- println(methodAll.fullName)
138
+ @ Test def checkScalaMethodPresenceVerbose : Unit = {
139
+ println(" \n Testing that all mentioned Scala methods exist" )
140
+ println( " ----------------------------------------------\n " )
64
141
65
- val methodBufferAlts = tp.member(newTermName(" buffer" )).asTerm.alternatives
142
+ val actualMethods = getPublicInstanceMethods(typeOf[rx.lang.scala.Observable [_]]).toSet
143
+ var good = 0
144
+ var bad = 0
145
+ for ((javaM, scalaM) <- SortedMap (correspondence.toSeq :_* )) {
146
+ if (actualMethods.contains(scalaM) || scalaM.charAt(0 ) == '[' ) {
147
+ good += 1
148
+ } else {
149
+ bad += 1
150
+ println(s " Warning: " )
151
+ println(s " $scalaM is NOT present in Scala Observable " )
152
+ println(s " $javaM is the method in Java Observable generating this warning " )
153
+ }
154
+ }
155
+ val status = if (bad == 0 ) " SUCCESS" else " BAD"
156
+ println(s " $status: $bad out of ${bad+ good} methods were not found in Scala Observable " )
157
+ }
158
+
159
+ @ Ignore // because we prefer the verbose version
160
+ @ Test def checkScalaMethodPresence : Unit = {
161
+ checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable [_]])
162
+ }
163
+
164
+ @ Test def checkJavaMethodPresence : Unit = {
165
+ println(" \n Testing that all mentioned Java methods exist" )
166
+ println( " ---------------------------------------------\n " )
167
+ checkMethodPresence(correspondence.keys, typeOf[rx.Observable [_]])
66
168
}
67
169
68
170
}
0 commit comments