aboutsummaryrefslogtreecommitdiff
path: root/test/test/DottyDocTests.scala
blob: 5161f98b8ed820a9480b94aa88e5ad372fb61ff8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
package test

import dotty.tools.dotc.Compiler
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.typer.FrontEnd
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.ast.Trees._

/** All tests need to extend this trait and define `source` and `assertion`
 *
 * Instances also need to be placed in the `DottyDocTests::tests` sequence to
 * be included in the run
 */
trait DottyDocTest extends DottyTest { self =>
  /** Source code in string format */
  def source: String

  /** A test to run against the resulting code */
  def assertion: PartialFunction[Tree[Untyped], Unit]

  private def defaultAssertion: PartialFunction[Tree[Untyped], Unit] = {
    case x => assert(false, "Couldn't match resulting AST to expected AST in: " + x.show)
  }

  private def compiler(assertion: PartialFunction[Tree[Untyped], Unit]) = new Compiler {
    override def phases = {
      val checker = new Phase {
        def phaseName = "assertionChecker"
        override def run(implicit ctx: Context): Unit =
          (assertion orElse defaultAssertion)(ctx.compilationUnit.untpdTree)
      }

      List(List(new FrontEnd)) ::: List(List(checker))
    }
  }

  def checkDocString(actual: Option[String], expected: String): Unit = actual match {
    case Some(str) => {
      assert(str == expected, s"""Docstring: "$str" didn't match expected "$expected"""")
    }
    case None =>
      assert(false, s"""No docstring found, expected: "$expected"""")
  }

  def run(): Unit = {
    val c = compiler(assertion)
    c.rootContext(ctx)
    c.newRun.compile(source)
    println(s"${self.getClass.getSimpleName.split("\\$").last} passed")
  }
}

/** Add tests to the `tests` sequence */
object DottyDocTests extends DottyTest {
  private[this] val tests = Seq(
    NoComment,
    SingleClassInPackage,
    MultipleOpenedOnSingleClassInPackage,
    MultipleClassesInPackage,
    SingleCaseClassWithoutPackage,
    SingleTraitWihoutPackage,
    MultipleTraitsWithoutPackage,
    MultipleMixedEntitiesWithPackage,
    NestedClass,
    NestedClassThenOuter,
    Objects,
    ObjectsNestedClass,
    PackageObject,
    MultipleDocStringsBeforeEntity,
    MultipleDocStringsBeforeAndAfter
  )

  def main(args: Array[String]): Unit = {
    println("------------ Testing DottyDoc  ------------")
    tests.foreach(_.run)
    println("--------- DottyDoc tests passed! ----------")
  }
}

case object NoComment extends DottyDocTest {
  override val source = "class Class"

  import dotty.tools.dotc.ast.untpd._
  override def assertion = {
    case PackageDef(_, Seq(c: TypeDef)) =>
      assert(c.rawComment == None, "Should not have a comment, mainly used for exhaustive tests")
  }
}

case object SingleClassInPackage extends DottyDocTest {
  override val source =
    """
    |package a
    |
    |/** Hello world! */
    |class Class(val x: String)
    """.stripMargin

    override def assertion = {
      case PackageDef(_, Seq(t @ TypeDef(name, _))) if name.toString == "Class" =>
        checkDocString(t.rawComment, "/** Hello world! */")
    }
}

case object MultipleOpenedOnSingleClassInPackage extends DottyDocTest {
  override val source =
    """
    |package a
    |
    |/** Hello /* multiple open */ world! */
    |class Class(val x: String)
    """.stripMargin

  override def assertion = {
    case PackageDef(_, Seq(t @ TypeDef(name, _))) if name.toString == "Class" =>
      checkDocString(t.rawComment, "/** Hello /* multiple open */ world! */")
  }
}

case object MultipleClassesInPackage extends DottyDocTest {
  override val source =
    """
    |package a
    |
    |/** Class1 docstring */
    |class Class1(val x: String)
    |
    |/** Class2 docstring */
    |class Class2(val x: String)
    """.stripMargin

  override def assertion = {
    case PackageDef(_, Seq(c1 @ TypeDef(_,_), c2 @ TypeDef(_,_))) => {
      checkDocString(c1.rawComment, "/** Class1 docstring */")
      checkDocString(c2.rawComment, "/** Class2 docstring */")
    }
  }
}

case object SingleCaseClassWithoutPackage extends DottyDocTest {
  override val source =
    """
    |/** Class without package */
    |case class Class(val x: Int)
    """.stripMargin

  override def assertion = {
    case PackageDef(_, Seq(t @ TypeDef(_,_))) => checkDocString(t.rawComment, "/** Class without package */")
  }
}

case object SingleTraitWihoutPackage extends DottyDocTest {
  override val source = "/** Trait docstring */\ntrait Trait"

  override def assertion = {
    case PackageDef(_, Seq(t @ TypeDef(_,_))) => checkDocString(t.rawComment, "/** Trait docstring */")
  }
}

case object MultipleTraitsWithoutPackage extends DottyDocTest {
  override val source =
    """
    |/** Trait1 docstring */
    |trait Trait1
    |
    |/** Trait2 docstring */
    |trait Trait2
    """.stripMargin

  override def assertion = {
    case PackageDef(_, Seq(t1 @ TypeDef(_,_), t2 @ TypeDef(_,_))) => {
      checkDocString(t1.rawComment, "/** Trait1 docstring */")
      checkDocString(t2.rawComment, "/** Trait2 docstring */")
    }
  }
}

case object MultipleMixedEntitiesWithPackage extends DottyDocTest {
  override val source =
    """
    |/** Trait1 docstring */
    |trait Trait1
    |
    |/** Class2 docstring */
    |class Class2(val x: Int)
    |
    |/** CaseClass3 docstring */
    |case class CaseClass3()
    |
    |case class NoComment()
    |
    |/** AbstractClass4 docstring */
    |abstract class AbstractClass4(val x: Int)
    """.stripMargin

  override def assertion = {
    case PackageDef(_, Seq(t1 @ TypeDef(_,_), c2 @ TypeDef(_,_), cc3 @ TypeDef(_,_), _, ac4 @ TypeDef(_,_))) => {
      checkDocString(t1.rawComment, "/** Trait1 docstring */")
      checkDocString(c2.rawComment, "/** Class2 docstring */")
      checkDocString(cc3.rawComment, "/** CaseClass3 docstring */")
      checkDocString(ac4.rawComment, "/** AbstractClass4 docstring */")
    }
  }
}

case object NestedClass extends DottyDocTest {
  override val source =
    """
    |/** Outer docstring */
    |class Outer {
    |  /** Inner docstring */
    |  class Inner(val x: Int)
    |}
    """.stripMargin

  override def assertion = {
    case PackageDef(_, Seq(outer @ TypeDef(_, tpl @ Template(_,_,_,_)))) =>
      checkDocString(outer.rawComment, "/** Outer docstring */")
      tpl.body match {
        case (inner @ TypeDef(_,_)) :: _ => checkDocString(inner.rawComment, "/** Inner docstring */")
        case _ => assert(false, "Couldn't find inner class")
      }
  }
}

case object NestedClassThenOuter extends DottyDocTest {
  override val source =
    """
    |/** Outer1 docstring */
    |class Outer1 {
    |  /** Inner docstring */
    |  class Inner(val x: Int)
    |}
    |
    |/** Outer2 docstring */
    |class Outer2
    """.stripMargin

  override def assertion = {
    case PackageDef(_, Seq(o1 @ TypeDef(_, tpl @ Template(_,_,_,_)), o2 @ TypeDef(_,_))) =>
      checkDocString(o1.rawComment, "/** Outer1 docstring */")
      checkDocString(o2.rawComment, "/** Outer2 docstring */")
      tpl.body match {
        case (inner @ TypeDef(_,_)) :: _ => checkDocString(inner.rawComment, "/** Inner docstring */")
        case _ => assert(false, "Couldn't find inner class")
      }
  }
}

case object Objects extends DottyDocTest {
  override val source =
    """
    |package p
    |
    |/** Object1 docstring */
    |object Object1
    |
    |/** Object2 docstring */
    |object Object2
    """.stripMargin

  override def assertion = {
    case p @ PackageDef(_, Seq(o1: MemberDef[Untyped], o2: MemberDef[Untyped])) =>
      assert(o1.name.toString == "Object1")
      checkDocString(o1.rawComment, "/** Object1 docstring */")
      assert(o2.name.toString == "Object2")
      checkDocString(o2.rawComment, "/** Object2 docstring */")
  }
}

case object ObjectsNestedClass extends DottyDocTest {
  override val source =
    """
    |package p
    |
    |/** Object1 docstring */
    |object Object1
    |
    |/** Object2 docstring */
    |object Object2 {
    |  class A1
    |  /** Inner docstring */
    |  class Inner
    |}
    """.stripMargin

    import dotty.tools.dotc.ast.untpd._
    override def assertion = {
      case p @ PackageDef(_, Seq(o1: ModuleDef, o2: ModuleDef)) =>
        assert(o1.name.toString == "Object1")
        checkDocString(o1.rawComment, "/** Object1 docstring */")
        assert(o2.name.toString == "Object2")
        checkDocString(o2.rawComment, "/** Object2 docstring */")

        o2.impl.body match {
          case _ :: (inner @ TypeDef(_,_)) :: _ => checkDocString(inner.rawComment, "/** Inner docstring */")
          case _ => assert(false, "Couldn't find inner class")
        }
    }
}

case object PackageObject extends DottyDocTest {
  override val source =
    """
    |/** Package object docstring */
    |package object foo {
    |  /** Boo docstring */
    |  case class Boo()
    |
    |  /** Trait docstring */
    |  trait Trait
    |
    |  /** InnerObject docstring */
    |  object InnerObject {
    |    /** InnerClass docstring */
    |    class InnerClass
    |  }
    |}
    """.stripMargin

  import dotty.tools.dotc.ast.untpd._
  override def assertion = {
    case PackageDef(_, Seq(p: ModuleDef)) => {
      checkDocString(p.rawComment, "/** Package object docstring */")

      p.impl.body match {
        case (b: TypeDef) :: (t: TypeDef) :: (o: ModuleDef) :: Nil => {
          checkDocString(b.rawComment, "/** Boo docstring */")
          checkDocString(t.rawComment, "/** Trait docstring */")
          checkDocString(o.rawComment, "/** InnerObject docstring */")
          checkDocString(o.impl.body.head.asInstanceOf[TypeDef].rawComment, "/** InnerClass docstring */")
        }
        case _ => assert(false, "Incorrect structure inside package object")
      }
    }
  }
}

case object MultipleDocStringsBeforeEntity extends DottyDocTest {
  override val source =
    """
    |/** First comment */
    |/** Second comment */
    |/** Real comment */
    |class Class
    """.stripMargin

  import dotty.tools.dotc.ast.untpd._
  override def assertion = {
    case PackageDef(_, Seq(c: TypeDef)) =>
      checkDocString(c.rawComment, "/** Real comment */")
  }
}

case object MultipleDocStringsBeforeAndAfter extends DottyDocTest {
  override val source =
    """
    |/** First comment */
    |/** Second comment */
    |/** Real comment */
    |class Class
    |/** Following comment 1 */
    |/** Following comment 2 */
    |/** Following comment 3 */
    """.stripMargin

  import dotty.tools.dotc.ast.untpd._
  override def assertion = {
    case PackageDef(_, Seq(c: TypeDef)) =>
      checkDocString(c.rawComment, "/** Real comment */")
  }

}